source: OpenRLabs-Git/deploy/rlabs-docker/web2py-rlabs/gluon/authapi.py @ 42095c5

mainqndtest v1.1.1
Last change on this file since 42095c5 was 42bd667, checked in by David Fuertes <dfuertes@…>, 4 years ago

Historial Limpio

  • Property mode set to 100755
File size: 43.6 KB
Line 
1# -*- coding: utf-8 -*-
2"""
3| This file is part of the web2py Web Framework
4| Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu>
5| License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
6"""
7from gluon._compat import long
8from gluon import current
9from gluon.storage import Messages, Settings, Storage
10from gluon.utils import web2py_uuid
11from gluon.validators import CRYPT, IS_EMAIL, IS_EQUAL_TO, IS_INT_IN_RANGE, IS_LOWER, IS_MATCH, IS_NOT_EMPTY, \
12    IS_NOT_IN_DB
13from pydal.objects import Table, Field, Row
14import datetime
15from gluon.settings import global_settings
16
17DEFAULT = lambda: None
18
19
20class AuthAPI(object):
21    """
22    AuthAPI is a barebones Auth implementation which does not have a concept of
23    HTML forms or redirects, emailing or even an URL, you are responsible for
24    all that if you use it.
25    The main Auth functions such as login, logout, register, profile are designed
26    in a Dict In -> Dict Out logic so, for instance, if you set
27    registration_requires_verification you are responsible for sending the key to
28    the user and even rolling back the transaction if you can't do it.
29
30    NOTES: * It does not support all the callbacks Traditional Auth does yet.
31             Some of the callbacks will not be supported.
32             Check the method signatures to find out which ones are supported.
33           * register_fields and profile_fields settings are ignored for now.
34
35    WARNING: No builtin CSRF protection whatsoever.
36    """
37
38    default_settings = {
39        'create_user_groups': 'user_%(id)s',
40        'email_case_sensitive': False,
41        'everybody_group_id': None,
42        'expiration': 3600,
43        'keep_session_onlogin': True,
44        'keep_session_onlogout': False,
45        'logging_enabled': True,
46        'login_after_registration': False,
47        'login_email_validate': True,
48        'login_userfield': None,
49        'logout_onlogout': None,
50        'long_expiration': 3600 * 24 * 30,
51        'ondelete': 'CASCADE',
52        'password_field': 'password',
53        'password_min_length': 4,
54        'registration_requires_approval': False,
55        'registration_requires_verification': False,
56        'renew_session_onlogin': True,
57        'renew_session_onlogout': True,
58        'table_event_name': 'auth_event',
59        'table_group_name': 'auth_group',
60        'table_membership_name': 'auth_membership',
61        'table_permission_name': 'auth_permission',
62        'table_user_name': 'auth_user',
63        'use_username': False,
64        'username_case_sensitive': True
65    }
66
67    default_messages = {
68        'add_group_log': 'Group %(group_id)s created',
69        'add_membership_log': None,
70        'add_permission_log': None,
71        'change_password_log': 'User %(id)s Password changed',
72        'del_group_log': 'Group %(group_id)s deleted',
73        'del_membership_log': None,
74        'del_permission_log': None,
75        'email_taken': 'This email already has an account',
76        'group_description': 'Group uniquely assigned to user %(id)s',
77        'has_membership_log': None,
78        'has_permission_log': None,
79        'invalid_email': 'Invalid email',
80        'key_verified': 'Key verified',
81        'invalid_login': 'Invalid login',
82        'invalid_password': 'Invalid password',
83        'invalid_user': 'Invalid user',
84        'invalid_key': 'Invalid key',
85        'invalid_username': 'Invalid username',
86        'logged_in': 'Logged in',
87        'logged_out': 'Logged out',
88        'login_failed_log': None,
89        'login_log': 'User %(id)s Logged-in',
90        'logout_log': 'User %(id)s Logged-out',
91        'mismatched_password': "Password fields don't match",
92        'password_changed': 'Password changed',
93        'profile_log': 'User %(id)s Profile updated',
94        'profile_updated': 'Profile updated',
95        'register_log': 'User %(id)s Registered',
96        'registration_pending': 'Registration is pending approval',
97        'registration_successful': 'Registration successful',
98        'registration_verifying': 'Registration needs verification',
99        'username_taken': 'Username already taken',
100        'verify_log': 'User %(id)s verified registration key'
101    }
102
103    def __init__(self, db=None, hmac_key=None, signature=True):
104        self.db = db
105        session = current.session
106        auth = session.auth
107        self.user_groups = auth and auth.user_groups or {}
108        now = current.request.now
109        # if we have auth info
110        #    if not expired it, used it
111        #    if expired, clear the session
112        # else, only clear auth info in the session
113        if auth:
114            delta = datetime.timedelta(days=0, seconds=auth.expiration)
115            if auth.last_visit and auth.last_visit + delta > now:
116                self.user = auth.user
117                # this is a trick to speed up sessions to avoid many writes
118                if (now - auth.last_visit).seconds > (auth.expiration // 10):
119                    auth.last_visit = now
120            else:
121                self.user = None
122                if session.auth:
123                    del session.auth
124                session.renew(clear_session=True)
125        else:
126            self.user = None
127            if session.auth:
128                del session.auth
129
130        settings = self.settings = Settings(self.__class__.default_settings)
131        settings.update(
132            extra_fields={},
133            hmac_key=hmac_key,
134        )
135        settings.lock_keys = True
136        messages = self.messages = Messages(current.T)
137        messages.update(self.default_messages)
138        messages.lock_keys = True
139        if signature is True:
140            self.define_signature()
141        else:
142            self.signature = signature or None
143
144    def __validate(self, value, requires):
145        if not isinstance(requires, (list, tuple)):
146            requires = [requires]
147        for validator in requires:
148            (value, error) = validator(value)
149            if error:
150                return (value, error)
151        return (value, None)
152
153    def _get_migrate(self, tablename, migrate=True):
154
155        if type(migrate).__name__ == 'str':
156            return (migrate + tablename + '.table')
157        elif not migrate:
158            return False
159        else:
160            return True
161
162    def _get_user_id(self):
163        """accessor for auth.user_id"""
164        return self.user and self.user.id or None
165
166    user_id = property(_get_user_id, doc="user.id or None")
167
168    def table_user(self):
169        return self.db[self.settings.table_user_name]
170
171    def table_group(self):
172        return self.db[self.settings.table_group_name]
173
174    def table_membership(self):
175        return self.db[self.settings.table_membership_name]
176
177    def table_permission(self):
178        return self.db[self.settings.table_permission_name]
179
180    def table_event(self):
181        return self.db[self.settings.table_event_name]
182
183    def define_signature(self):
184        db = self.db
185        settings = self.settings
186        request = current.request
187        T = current.T
188        reference_user = 'reference %s' % settings.table_user_name
189
190        def lazy_user(auth=self):
191            return auth.user_id
192
193        def represent(id, record=None, s=settings):
194            try:
195                user = s.table_user(id)
196                return '%s %s' % (user.get("first_name", user.get("email")),
197                                  user.get("last_name", ''))
198            except:
199                return id
200        ondelete = self.settings.ondelete
201        self.signature = Table(
202            self.db, 'auth_signature',
203            Field('is_active', 'boolean',
204                  default=True,
205                  readable=False, writable=False,
206                  label=T('Is Active')),
207            Field('created_on', 'datetime',
208                  default=request.now,
209                  writable=False, readable=False,
210                  label=T('Created On')),
211            Field('created_by',
212                  reference_user,
213                  default=lazy_user, represent=represent,
214                  writable=False, readable=False,
215                  label=T('Created By'), ondelete=ondelete),
216            Field('modified_on', 'datetime',
217                  update=request.now, default=request.now,
218                  writable=False, readable=False,
219                  label=T('Modified On')),
220            Field('modified_by',
221                  reference_user, represent=represent,
222                  default=lazy_user, update=lazy_user,
223                  writable=False, readable=False,
224                  label=T('Modified By'),  ondelete=ondelete))
225
226    def define_tables(self, username=None, signature=None, migrate=None,
227                      fake_migrate=None):
228        """
229        To be called unless tables are defined manually
230
231        Examples:
232            Use as::
233
234                # defines all needed tables and table files
235                # 'myprefix_auth_user.table', ...
236                auth.define_tables(migrate='myprefix_')
237
238                # defines all needed tables without migration/table files
239                auth.define_tables(migrate=False)
240
241        """
242
243        db = self.db
244        if migrate is None:
245            migrate = db._migrate
246        if fake_migrate is None:
247            fake_migrate = db._fake_migrate
248
249        settings = self.settings
250        if username is None:
251            username = settings.use_username
252        else:
253            settings.use_username = username
254
255        if not self.signature:
256            self.define_signature()
257        if signature is True:
258            signature_list = [self.signature]
259        elif not signature:
260            signature_list = []
261        elif isinstance(signature, Table):
262            signature_list = [signature]
263        else:
264            signature_list = signature
265        self._table_signature_list = signature_list  # Should it defined in __init__ first??
266
267        is_not_empty = IS_NOT_EMPTY(error_message=self.messages.is_empty)
268        is_crypted = CRYPT(key=settings.hmac_key,
269                           min_length=settings.password_min_length)
270        is_unique_email = [
271            IS_EMAIL(error_message=self.messages.invalid_email),
272            IS_NOT_IN_DB(db, '%s.email' % settings.table_user_name,
273                         error_message=self.messages.email_taken)]
274        if not settings.email_case_sensitive:
275            is_unique_email.insert(1, IS_LOWER())
276        if settings.table_user_name not in db.tables:
277            passfield = settings.password_field
278            extra_fields = settings.extra_fields.get(
279                settings.table_user_name, []) + signature_list
280            # cas_provider Will always be None here but we compare it anyway so subclasses can use our define_tables
281            if username or settings.cas_provider:
282                is_unique_username = \
283                    [IS_MATCH('[\w\.\-]+', strict=True,
284                              error_message=self.messages.invalid_username),
285                     IS_NOT_IN_DB(db, '%s.username' % settings.table_user_name,
286                                  error_message=self.messages.username_taken)]
287                if not settings.username_case_sensitive:
288                    is_unique_username.insert(1, IS_LOWER())
289                db.define_table(
290                    settings.table_user_name,
291                    Field('first_name', length=128, default='',
292                          label=self.messages.label_first_name,
293                          requires=is_not_empty),
294                    Field('last_name', length=128, default='',
295                          label=self.messages.label_last_name,
296                          requires=is_not_empty),
297                    Field('email', length=512, default='',
298                          label=self.messages.label_email,
299                          requires=is_unique_email),
300                    Field('username', length=128, default='',
301                          label=self.messages.label_username,
302                          requires=is_unique_username),
303                    Field(passfield, 'password', length=512,
304                          readable=False, label=self.messages.label_password,
305                          requires=[is_crypted]),
306                    Field('registration_key', length=512,
307                          writable=False, readable=False, default='',
308                          label=self.messages.label_registration_key),
309                    Field('reset_password_key', length=512,
310                          writable=False, readable=False, default='',
311                          label=self.messages.label_reset_password_key),
312                    Field('registration_id', length=512,
313                          writable=False, readable=False, default='',
314                          label=self.messages.label_registration_id),
315                    *extra_fields,
316                    **dict(
317                        migrate=self._get_migrate(settings.table_user_name,
318                                                  migrate),
319                        fake_migrate=fake_migrate,
320                        format='%(username)s'))
321            else:
322                db.define_table(
323                    settings.table_user_name,
324                    Field('first_name', length=128, default='',
325                          label=self.messages.label_first_name,
326                          requires=is_not_empty),
327                    Field('last_name', length=128, default='',
328                          label=self.messages.label_last_name,
329                          requires=is_not_empty),
330                    Field('email', length=512, default='',
331                          label=self.messages.label_email,
332                          requires=is_unique_email),
333                    Field(passfield, 'password', length=512,
334                          readable=False, label=self.messages.label_password,
335                          requires=[is_crypted]),
336                    Field('registration_key', length=512,
337                          writable=False, readable=False, default='',
338                          label=self.messages.label_registration_key),
339                    Field('reset_password_key', length=512,
340                          writable=False, readable=False, default='',
341                          label=self.messages.label_reset_password_key),
342                    Field('registration_id', length=512,
343                          writable=False, readable=False, default='',
344                          label=self.messages.label_registration_id),
345                    *extra_fields,
346                    **dict(
347                        migrate=self._get_migrate(settings.table_user_name,
348                                                  migrate),
349                        fake_migrate=fake_migrate,
350                        format='%(first_name)s %(last_name)s (%(id)s)'))
351        reference_table_user = 'reference %s' % settings.table_user_name
352        if settings.table_group_name not in db.tables:
353            extra_fields = settings.extra_fields.get(
354                settings.table_group_name, []) + signature_list
355            db.define_table(
356                settings.table_group_name,
357                Field('role', length=512, default='',
358                      label=self.messages.label_role,
359                      requires=IS_NOT_IN_DB(db, '%s.role' % settings.table_group_name)),
360                Field('description', 'text',
361                      label=self.messages.label_description),
362                *extra_fields,
363                **dict(
364                    migrate=self._get_migrate(
365                        settings.table_group_name, migrate),
366                    fake_migrate=fake_migrate,
367                    format='%(role)s (%(id)s)'))
368        reference_table_group = 'reference %s' % settings.table_group_name
369        if settings.table_membership_name not in db.tables:
370            extra_fields = settings.extra_fields.get(
371                settings.table_membership_name, []) + signature_list
372            db.define_table(
373                settings.table_membership_name,
374                Field('user_id', reference_table_user,
375                      label=self.messages.label_user_id),
376                Field('group_id', reference_table_group,
377                      label=self.messages.label_group_id),
378                *extra_fields,
379                **dict(
380                    migrate=self._get_migrate(
381                        settings.table_membership_name, migrate),
382                    fake_migrate=fake_migrate))
383        if settings.table_permission_name not in db.tables:
384            extra_fields = settings.extra_fields.get(
385                settings.table_permission_name, []) + signature_list
386            db.define_table(
387                settings.table_permission_name,
388                Field('group_id', reference_table_group,
389                      label=self.messages.label_group_id),
390                Field('name', default='default', length=512,
391                      label=self.messages.label_name,
392                      requires=is_not_empty),
393                Field('table_name', length=512,
394                      label=self.messages.label_table_name),
395                Field('record_id', 'integer', default=0,
396                      label=self.messages.label_record_id,
397                      requires=IS_INT_IN_RANGE(0, 10 ** 9)),
398                *extra_fields,
399                **dict(
400                    migrate=self._get_migrate(
401                        settings.table_permission_name, migrate),
402                    fake_migrate=fake_migrate))
403        if settings.table_event_name not in db.tables:
404            db.define_table(
405                settings.table_event_name,
406                Field('time_stamp', 'datetime',
407                      default=current.request.now,
408                      label=self.messages.label_time_stamp),
409                Field('client_ip',
410                      default=current.request.client,
411                      label=self.messages.label_client_ip),
412                Field('user_id', reference_table_user, default=None,
413                      label=self.messages.label_user_id),
414                Field('origin', default='auth', length=512,
415                      label=self.messages.label_origin,
416                      requires=is_not_empty),
417                Field('description', 'text', default='',
418                      label=self.messages.label_description,
419                      requires=is_not_empty),
420                *settings.extra_fields.get(settings.table_event_name, []),
421                **dict(
422                    migrate=self._get_migrate(
423                        settings.table_event_name, migrate),
424                    fake_migrate=fake_migrate))
425
426        return self
427
428    def log_event(self, description, vars=None, origin='auth'):
429        """
430        Examples:
431            Use as::
432
433                auth.log_event(description='this happened', origin='auth')
434
435        """
436        if not self.settings.logging_enabled or not description:
437            return
438        elif self.is_logged_in():
439            user_id = self.user.id
440        else:
441            user_id = None  # user unknown
442        vars = vars or {}
443        # log messages should not be translated
444        if type(description).__name__ == 'lazyT':
445            description = description.m
446        if not user_id or self.table_user()[user_id]:
447            self.table_event().insert(
448                description=str(description % vars), origin=origin, user_id=user_id)
449
450    def id_group(self, role):
451        """
452        Returns the group_id of the group specified by the role
453        """
454        rows = self.db(self.table_group().role == role).select()
455        if not rows:
456            return None
457        return rows[0].id
458
459    def user_group(self, user_id=None):
460        """
461        Returns the group_id of the group uniquely associated to this user
462        i.e. `role=user:[user_id]`
463        """
464        return self.id_group(self.user_group_role(user_id))
465
466    def user_group_role(self, user_id=None):
467        if not self.settings.create_user_groups:
468            return None
469        if user_id:
470            user = self.table_user()[user_id]
471        else:
472            user = self.user
473        return self.settings.create_user_groups % user
474
475    def add_group(self, role, description=''):
476        """
477        Creates a group associated to a role
478        """
479        group_id = self.table_group().insert(role=role, description=description)
480        self.log_event(self.messages['add_group_log'], dict(group_id=group_id, role=role))
481        return group_id
482
483    def del_group(self, group_id):
484        """
485        Deletes a group
486        """
487        self.db(self.table_group().id == group_id).delete()
488        self.db(self.table_membership().group_id == group_id).delete()
489        self.db(self.table_permission().group_id == group_id).delete()
490        if group_id in self.user_groups:
491            del self.user_groups[group_id]
492        self.log_event(self.messages.del_group_log, dict(group_id=group_id))
493
494    def update_groups(self):
495        if not self.user:
496            return
497        user_groups = self.user_groups = {}
498        if current.session.auth:
499            current.session.auth.user_groups = self.user_groups
500        table_group = self.table_group()
501        table_membership = self.table_membership()
502        memberships = self.db(
503            table_membership.user_id == self.user.id).select()
504        for membership in memberships:
505            group = table_group(membership.group_id)
506            if group:
507                user_groups[membership.group_id] = group.role
508
509    def add_membership(self, group_id=None, user_id=None, role=None):
510        """
511        Gives user_id membership of group_id or role
512        if user is None than user_id is that of current logged in user
513        """
514
515        group_id = group_id or self.id_group(role)
516        try:
517            group_id = int(group_id)
518        except:
519            group_id = self.id_group(group_id)  # interpret group_id as a role
520        if not user_id and self.user:
521            user_id = self.user.id
522        if not group_id:
523            raise ValueError('group_id not provided or invalid')
524        if not user_id:
525            raise ValueError('user_id not provided or invalid')
526        membership = self.table_membership()
527        db = membership._db
528        record = db((membership.user_id == user_id) &
529                    (membership.group_id == group_id),
530                    ignore_common_filters=True).select().first()
531        if record:
532            if hasattr(record, 'is_active') and not record.is_active:
533                record.update_record(is_active=True)
534            return record.id
535        else:
536            id = membership.insert(group_id=group_id, user_id=user_id)
537        if role and user_id == self.user_id:
538            self.user_groups[group_id] = role
539        else:
540            self.update_groups()
541        self.log_event(self.messages['add_membership_log'],
542                       dict(user_id=user_id, group_id=group_id))
543        return id
544
545    def del_membership(self, group_id=None, user_id=None, role=None):
546        """
547        Revokes membership from group_id to user_id
548        if user_id is None than user_id is that of current logged in user
549        """
550
551        group_id = group_id or self.id_group(role)
552        try:
553            group_id = int(group_id)
554        except:
555            group_id = self.id_group(group_id)  # interpret group_id as a role
556        if not user_id and self.user:
557            user_id = self.user.id
558        membership = self.table_membership()
559        self.log_event(self.messages['del_membership_log'],
560                       dict(user_id=user_id, group_id=group_id))
561        ret = self.db(membership.user_id == user_id)(membership.group_id == group_id).delete()
562        if group_id in self.user_groups and user_id == self.user_id:
563            del self.user_groups[group_id]
564        return ret
565
566    def has_membership(self, group_id=None, user_id=None, role=None, cached=False):
567        """
568        Checks if user is member of group_id or role
569
570        NOTE: To avoid database query at each page load that use auth.has_membership, someone can use cached=True.
571              If cached is set to True has_membership() check group_id or role only against auth.user_groups variable
572              which is populated properly only at login time. This means that if an user membership change during a
573              given session the user has to log off and log in again in order to auth.user_groups to be properly
574              recreated and reflecting the user membership modification. There is one exception to this log off and
575              log in process which is in case that the user change his own membership, in this case auth.user_groups
576              can be properly update for the actual connected user because web2py has access to the proper session
577              user_groups variable. To make use of this exception someone has to place an "auth.update_groups()"
578              instruction in his app code to force auth.user_groups to be updated. As mention this will only work if it
579              the user itself that change it membership not if another user, let say an administrator, change someone
580              else's membership.
581        """
582        if not user_id and self.user:
583            user_id = self.user.id
584        if cached:
585            id_role = group_id or role
586            r = (user_id and id_role in self.user_groups.values()) or (user_id and id_role in self.user_groups)
587        else:
588            group_id = group_id or self.id_group(role)
589            try:
590                group_id = int(group_id)
591            except:
592                group_id = self.id_group(group_id)  # interpret group_id as a role
593            membership = self.table_membership()
594            if group_id and user_id and self.db((membership.user_id == user_id) &
595                                                (membership.group_id == group_id)).select():
596                r = True
597            else:
598                r = False
599        self.log_event(self.messages['has_membership_log'],
600                       dict(user_id=user_id, group_id=group_id, check=r))
601        return r
602
603    def add_permission(self,
604                       group_id,
605                       name='any',
606                       table_name='',
607                       record_id=0,
608                       ):
609        """
610        Gives group_id 'name' access to 'table_name' and 'record_id'
611        """
612
613        permission = self.table_permission()
614        if group_id == 0:
615            group_id = self.user_group()
616        record = self.db((permission.group_id == group_id) &
617                         (permission.name == name) &
618                         (permission.table_name == str(table_name)) &
619                         (permission.record_id == long(record_id)),
620                         ignore_common_filters=True
621                         ).select(limitby=(0, 1), orderby_on_limitby=False).first()
622        if record:
623            if hasattr(record, 'is_active') and not record.is_active:
624                record.update_record(is_active=True)
625            id = record.id
626        else:
627            id = permission.insert(group_id=group_id, name=name,
628                                   table_name=str(table_name),
629                                   record_id=long(record_id))
630        self.log_event(self.messages['add_permission_log'],
631                       dict(permission_id=id, group_id=group_id,
632                            name=name, table_name=table_name,
633                            record_id=record_id))
634        return id
635
636    def del_permission(self,
637                       group_id,
638                       name='any',
639                       table_name='',
640                       record_id=0,
641                       ):
642        """
643        Revokes group_id 'name' access to 'table_name' and 'record_id'
644        """
645
646        permission = self.table_permission()
647        self.log_event(self.messages['del_permission_log'],
648                       dict(group_id=group_id, name=name,
649                            table_name=table_name, record_id=record_id))
650        return self.db(permission.group_id ==
651                       group_id)(permission.name ==
652                                 name)(permission.table_name ==
653                                       str(table_name))(permission.record_id ==
654                                                        long(record_id)).delete()
655
656    def has_permission(self,
657                       name='any',
658                       table_name='',
659                       record_id=0,
660                       user_id=None,
661                       group_id=None,
662                       ):
663        """
664        Checks if user_id or current logged in user is member of a group
665        that has 'name' permission on 'table_name' and 'record_id'
666        if group_id is passed, it checks whether the group has the permission
667        """
668
669        if not group_id and self.settings.everybody_group_id and \
670                self.has_permission(name, table_name, record_id, user_id=None,
671                                    group_id=self.settings.everybody_group_id):
672                return True
673
674        if not user_id and not group_id and self.user:
675            user_id = self.user.id
676        if user_id:
677            membership = self.table_membership()
678            rows = self.db(membership.user_id == user_id).select(membership.group_id)
679            groups = set([row.group_id for row in rows])
680            if group_id and group_id not in groups:
681                return False
682        else:
683            groups = set([group_id])
684        permission = self.table_permission()
685        rows = self.db(permission.name ==
686                       name)(permission.table_name ==
687                             str(table_name))(permission.record_id ==
688                                              record_id).select(permission.group_id)
689        groups_required = set([row.group_id for row in rows])
690        if record_id:
691            rows = self.db(permission.name ==
692                           name)(permission.table_name ==
693                                 str(table_name))(permission.record_id ==
694                                                  0).select(permission.group_id)
695            groups_required = groups_required.union(set([row.group_id for row in rows]))
696        if groups.intersection(groups_required):
697            r = True
698        else:
699            r = False
700        if user_id:
701            self.log_event(self.messages['has_permission_log'],
702                           dict(user_id=user_id, name=name,
703                                table_name=table_name, record_id=record_id))
704        return r
705
706    def is_logged_in(self):
707        """
708        Checks if the user is logged in and returns True/False.
709        If so user is in auth.user as well as in session.auth.user
710        """
711        if self.user:
712            return True
713        return False
714
715    def _update_session_user(self, user):
716        if global_settings.web2py_runtime_gae:
717            user = Row(self.table_user()._filter_fields(user, id=True))
718            delattr(user, self.settings.password_field)
719        else:
720            user = Row(user)
721            for key in list(user.keys()):
722                value = user[key]
723                if callable(value) or key == self.settings.password_field:
724                    delattr(user, key)
725        current.session.auth = Storage(user=user,
726                                       last_visit=current.request.now,
727                                       expiration=self.settings.expiration,
728                                       hmac_key=web2py_uuid())
729        return user
730
731    def login_user(self, user):
732        """
733        Logins the `user = db.auth_user(id)`
734        """
735        user = self._update_session_user(user)
736        if self.settings.renew_session_onlogin:
737            current.session.renew(clear_session=not self.settings.keep_session_onlogin)
738        self.user = user
739        self.update_groups()
740
741    def login(self, log=DEFAULT, **kwargs):
742        """
743        Login a user
744
745        Keyword Args:
746            username/email/name_of_your_username_field (string) - username
747            password/name_of_your_passfield (string) - user's password
748            remember_me (boolean) - extend the duration of the login to settings.long_expiration
749        """
750        settings = self.settings
751        session = current.session
752        table_user = self.table_user()
753
754        if 'username' in table_user.fields or \
755                not settings.login_email_validate:
756            userfield_validator = IS_NOT_EMPTY(error_message=self.messages.is_empty)
757            if not settings.username_case_sensitive:
758                userfield_validator = [IS_LOWER(), userfield_validator]
759        else:
760            userfield_validator = IS_EMAIL(error_message=self.messages.invalid_email)
761            if not settings.email_case_sensitive:
762                userfield_validator = [IS_LOWER(), userfield_validator]
763
764        passfield = settings.password_field
765
766        if log is DEFAULT:
767            log = self.messages['login_log']
768
769        user = None
770
771        # Setup the default field used for the userfield
772        if self.settings.login_userfield:
773            userfield = self.settings.login_userfield
774        else:
775            if 'username' in table_user.fields:
776                userfield = 'username'
777            else:
778                userfield = 'email'
779
780        # Get the userfield from kwargs and validate it
781        userfield_value = kwargs.get(userfield)
782        if userfield_value is None:
783            raise KeyError('%s not found in kwargs' % userfield)
784
785        validated, error = self.__validate(userfield_value, userfield_validator)
786
787        if error:
788            return {'errors': {userfield: error}, 'message': self.messages.invalid_login, 'user': None}
789
790        # Get the user for this userfield and check it
791        user = table_user(**{userfield: validated})
792
793        if user is None:
794            return {'errors': {userfield: self.messages.invalid_user},
795                    'message': self.messages.invalid_login, 'user': None}
796
797        if (user.registration_key or '').startswith('pending'):
798            return {'errors': None, 'message': self.messages.registration_pending, 'user': None}
799        elif user.registration_key in ('disabled', 'blocked'):
800            return {'errors': None, 'message': self.messages.login_disabled, 'user': None}
801        elif (user.registration_key is not None and user.registration_key.strip()):
802            return {'errors': None, 'message': self.messages.registration_verifying, 'user': None}
803
804        # Finally verify the password
805        passfield = settings.password_field
806        password = table_user[passfield].validate(kwargs.get(passfield, ''), None)[0]
807
808        if password == user[passfield]:
809            self.login_user(user)
810            session.auth.expiration = \
811                kwargs.get('remember_me', False) and \
812                settings.long_expiration or \
813                settings.expiration
814            session.auth.remember_me = kwargs.get('remember_me', False)
815            self.log_event(log, user)
816            return {'errors': None, 'message': self.messages.logged_in,
817                    'user': {k: user[k] for k in table_user.fields if table_user[k].readable}}
818        else:
819            self.log_event(self.messages['login_failed_log'], kwargs)
820            return {'errors': {passfield: self.messages.invalid_password},
821                    'message': self.messages.invalid_login, 'user': None}
822
823    def logout(self, log=DEFAULT, onlogout=DEFAULT, **kwargs):
824        """
825        Logs out user
826        """
827        settings = self.settings
828        session = current.session
829
830        if onlogout is DEFAULT:
831            onlogout = settings.logout_onlogout
832        if onlogout:
833            onlogout(self.user)
834        if log is DEFAULT:
835            log = self.messages['logout_log']
836        if self.user:
837            self.log_event(log, self.user)
838
839        session.auth = None
840        self.user = None
841        if settings.renew_session_onlogout:
842            session.renew(clear_session=not settings.keep_session_onlogout)
843
844        return {'errors': None, 'message': self.messages.logged_out, 'user': None}
845
846    def register(self, log=DEFAULT, **kwargs):
847        """
848        Register a user.
849        """
850
851        table_user = self.table_user()
852        settings = self.settings
853
854        if self.is_logged_in():
855            raise AssertionError('User trying to register is logged in')
856
857        if log is DEFAULT:
858            log = self.messages['register_log']
859
860        if self.settings.login_userfield:
861            userfield = self.settings.login_userfield
862        elif 'username' in table_user.fields:
863            userfield = 'username'
864        else:
865            userfield = 'email'
866
867        # Ensure the username field is unique.
868        unique_validator = IS_NOT_IN_DB(self.db, table_user[userfield])
869        userfield_validator = table_user[userfield].requires
870        if userfield_validator is None:
871            userfield_validator = unique_validator
872        elif isinstance(userfield_validator, (list, tuple)):
873            if not any([isinstance(validator, IS_NOT_IN_DB) for validator in
874                        userfield_validator]):
875                if isinstance(userfield_validator, list):
876                    userfield_validator.append(unique_validator)
877                else:
878                    userfield_validator += (unique_validator, )
879        elif not isinstance(userfield_validator, IS_NOT_IN_DB):
880            userfield_validator = [userfield_validator, unique_validator]
881        table_user[userfield].requires = userfield_validator
882
883        passfield = settings.password_field
884
885        try:  # Make sure we have our original minimum length
886            table_user[passfield].requires[-1].min_length = settings.password_min_length
887        except:
888            pass
889
890        key = web2py_uuid()
891        if settings.registration_requires_approval:
892            key = 'pending-' + key
893
894        table_user.registration_key.default = key
895
896        result = table_user.validate_and_insert(**kwargs)
897        if result.errors:
898            return {'errors': result.errors.as_dict(), 'message': None, 'user': None}
899
900        user = table_user[result.id]
901
902        message = self.messages.registration_successful
903
904        if settings.create_user_groups:
905            d = user.as_dict()
906            description = self.messages.group_description % d
907            group_id = self.add_group(settings.create_user_groups % d, description)
908            self.add_membership(group_id, result.id)
909
910        if self.settings.everybody_group_id:
911            self.add_membership(self.settings.everybody_group_id, result)
912
913        if settings.registration_requires_verification:
914            d = {k: user[k] for k in table_user.fields if table_user[k].readable}
915            d['key'] = key
916            if settings.login_after_registration and not settings.registration_requires_approval:
917                self.login_user(user)
918            return {'errors': None, 'message': None, 'user': d}
919
920        if settings.registration_requires_approval:
921            user.update_record(registration_key='pending')
922            message = self.messages.registration_pending
923        elif settings.login_after_registration:
924            user.update_record(registration_key='')
925            self.login_user(user)
926            message = self.messages.logged_in
927
928        self.log_event(log, user)
929
930        return {'errors': None, 'message': message,
931                'user': {k: user[k] for k in table_user.fields if table_user[k].readable}}
932
933    def profile(self, log=DEFAULT, **kwargs):
934        """
935        Lets the user change his/her profile
936        """
937
938        table_user = self.table_user()
939        settings = self.settings
940        table_user[settings.password_field].writable = False
941
942        if not self.is_logged_in():
943            raise AssertionError('User is not logged in')
944
945        if not kwargs:
946            user = table_user[self.user.id]
947            return {'errors': None, 'message': None,
948                    'user': {k: user[k] for k in table_user.fields if table_user[k].readable}}
949
950        result = self.db(table_user.id == self.user.id).validate_and_update(**kwargs)
951        user = table_user[self.user.id]
952
953        if result.errors:
954            return {'errors': result.errors, 'message': None,
955                    'user': {k: user[k] for k in table_user.fields if table_user[k].readable}}
956
957        if log is DEFAULT:
958            log = self.messages['profile_log']
959
960        self.log_event(log, user)
961        self._update_session_user(user)
962        return {'errors': None, 'message': self.messages.profile_updated,
963                'user': {k: user[k] for k in table_user.fields if table_user[k].readable}}
964
965    def change_password(self, log=DEFAULT, **kwargs):
966        """
967        Lets the user change password
968
969        Keyword Args:
970            old_password (string) - User's current password
971            new_password (string) - User's new password
972            new_password2 (string) - Verify the new password
973        """
974        settings = self.settings
975        messages = self.messages
976
977        if not self.is_logged_in():
978            raise AssertionError('User is not logged in')
979
980        db = self.db
981        table_user = self.table_user()
982        s = db(table_user.id == self.user.id)
983
984        request = current.request
985        session = current.session
986        passfield = settings.password_field
987
988        requires = table_user[passfield].requires
989        if not isinstance(requires, (list, tuple)):
990            requires = [requires]
991        requires = [t for t in requires if isinstance(t, CRYPT)]
992        if requires:
993            requires[0] = CRYPT(**requires[0].__dict__) # Copy the existing CRYPT attributes
994            requires[0].min_length = 0 # But do not enforce minimum length for the old password
995
996        old_password = kwargs.get('old_password', '')
997        new_password = kwargs.get('new_password', '')
998        new_password2 = kwargs.get('new_password2', '')
999
1000        validator_old = requires
1001        validator_pass2 = IS_EQUAL_TO(new_password, error_message=messages.mismatched_password)
1002
1003        old_password, error_old = self.__validate(old_password, validator_old)
1004        new_password2, error_new2 = self.__validate(new_password2, validator_pass2)
1005
1006        errors = {}
1007        if error_old:
1008            errors['old_password'] = error_old
1009        if error_new2:
1010            errors['new_password2'] = error_new2
1011        if errors:
1012            return {'errors': errors, 'message': None}
1013
1014        current_user = s.select(limitby=(0, 1), orderby_on_limitby=False).first()
1015        if not old_password == current_user[passfield]:
1016            return {'errors': {'old_password': messages.invalid_password}, 'message': None}
1017        else:
1018            d = {passfield: new_password}
1019            resp = s.validate_and_update(**d)
1020            if resp.errors:
1021                return {'errors': {'new_password': resp.errors[passfield]}, 'message': None}
1022            if log is DEFAULT:
1023                log = messages['change_password_log']
1024            self.log_event(log, self.user)
1025            return {'errors': None, 'message': messages.password_changed}
1026
1027    def verify_key(self,
1028                   key=None,
1029                   ignore_approval=False,
1030                   log=DEFAULT,
1031                   ):
1032        """
1033        Verify a given registration_key actually exists in the user table.
1034        Resets the key to empty string '' or 'pending' if
1035        setttings.registration_requires_approval is true.
1036
1037        Keyword Args:
1038            key (string) - User's registration key
1039        """
1040        table_user = self.table_user()
1041        user = table_user(registration_key=key)
1042        if (user is None) or (key is None):
1043            return {'errors': {'key': self.messages.invalid_key}, 'message': self.messages.invalid_key}
1044
1045        if self.settings.registration_requires_approval:
1046            user.update_record(registration_key='pending')
1047            result = {'errors': None, 'message': self.messages.registration_pending}
1048        else:
1049            user.update_record(registration_key='')
1050            result = {'errors': None, 'message': self.messages.key_verified}
1051        # make sure session has same user.registration_key as db record
1052        if current.session.auth and current.session.auth.user:
1053            current.session.auth.user.registration_key = user.registration_key
1054        if log is DEFAULT:
1055            log = self.messages['verify_log']
1056        self.log_event(log, user)
1057        return result
Note: See TracBrowser for help on using the repository browser.