Changeset 1d92bf2 in OpenWorkouts-current


Ignore:
Timestamp:
Dec 16, 2018, 1:07:04 AM (5 years ago)
Author:
borja <borja@…>
Branches:
current, feature/docs, master
Children:
6560b8f
Parents:
929097a
Message:

(#37) Allow login using email address instead of username:

  • Use user uids as keys in the root folder for referencing user objects (instead of username)
  • Use uids for referencing users all over the place (auth, permissions, traversal urls, etc)
  • Replaced the username concept with nickname. This nickname will be used as a shortcut to access "public profile" pages for users
  • Reworked lots of basic methods in the OpenWorkouts root object (s/username/nickname, marked as properties some methods like users, emails, etc)
  • Added new add_user() and delete_user() helpers to the OpenWorkouts root object
  • Fixed bug in the dashboard redirect view, causing an endless loop if an authenticated user does not exist anymore when loading a page.
  • Lots of tests fixes, adaptations and catch up.
Location:
ow
Files:
16 edited

Legend:

Unmodified
Added
Removed
  • ow/models/root.py

    r929097a r1d92bf2  
    33from repoze.folder import Folder
    44from repoze.catalog.indexes.field import CatalogFieldIndex
     5from repoze.catalog.query import Eq
    56
    67from pyramid.security import Allow, Everyone
    78
    89from ow.models.user import User
    9 from ow.catalog import get_catalog
     10from ow.catalog import (
     11    get_catalog,
     12    install_catalog,
     13    reindex_object,
     14    remove_from_catalog,
     15    resources_from_query_results
     16)
    1017
    1118
     
    1421    Root object, contains basically all the users, and in turn
    1522    the users contain their workouts.
     23
     24    Users are stored in a dict-like structure, using a string containing
     25    the user id (uid) as a key, and the User object as the value.
    1626    """
    1727
     
    2030    __acl__ = [
    2131               (Allow, Everyone, 'view'),
    22                (Allow, 'group:admins', 'edit')
     32               (Allow, 'admins', 'edit')
    2333              ]
     34
     35    def __init__(self, **kw):
     36        install_catalog(self)
     37        super(OpenWorkouts, self).__init__(**kw)
    2438
    2539    def _get_catalog_indexes(self):
    2640        indexes = {
     41            'email': CatalogFieldIndex('email'),
    2742            'sport': CatalogFieldIndex('sport'),
    2843        }
    2944        return indexes
    3045
    31     def all_usernames(self):
     46    def reindex(self, obj):
    3247        """
    33         Return all available usernames
     48        Reindex the given object in the catalog
    3449        """
    35         return self.keys()
     50        reindex_object(self.catalog, obj)
    3651
    37     def lowercase_usernames(self):
     52    def query(self, query):
    3853        """
    39         Return all available usernames in lower case. Useful for
    40         username uniqueness validation on signup
     54        Execute the given query on the catalog, returning the results
     55        (generator with events found or empty list if no results were found)
    4156        """
    42         return [name.lower() for name in self.keys()]
     57        catalog = get_catalog(self)
     58        number, results = catalog.query(query)
     59        if number:
     60            return resources_from_query_results(catalog, results, self)
     61        return []
    4362
     63    def add_user(self, user):
     64        self.add(str(user.uid), user)
     65        self.reindex(user)
     66
     67    def del_user(self, user):
     68        remove_from_catalog(self.catalog, user)
     69        self.remove(str(user.uid))
     70
     71    def get_user_by_uid(self, uid):
     72        return self.get(str(uid), None)
     73
     74    def get_user_by_email(self, email):
     75        if email is not None:
     76            # for some reason, when searching for None
     77            # the catalog will return all users
     78            res = self.query(Eq('email', email))
     79            if res:
     80                return next(res)
     81        return None
     82
     83    @property
     84    def users(self):
     85        """
     86        Return all user objects
     87        """
     88        return [user for user in self.values() if isinstance(user, User)]
     89
     90    @property
     91    def all_nicknames(self):
     92        """
     93        Return all available nicknames
     94        """
     95        return [user.nickname for user in self.users if user.nickname]
     96
     97    @property
     98    def lowercase_nicknames(self):
     99        """
     100        Return all available nicknames in lower case. Useful for
     101        nickname uniqueness validation on signup
     102        """
     103        return [nick.lower() for nick in self.all_nicknames]
     104
     105    @property
    44106    def emails(self):
    45107        """
    46108        Return all emails currently in use by users
    47109        """
    48         return [u.email for u in self.users()]
     110        return [user.email for user in self.users]
    49111
     112    @property
    50113    def lowercase_emails(self):
    51114        """
     
    54117        against the currently used addresses
    55118        """
    56         return [u.email.lower() for u in self.users()]
    57 
    58     def users(self):
    59         """
    60         Return all user objects
    61         """
    62         return [u for u in self.values() if isinstance(u, User)]
    63 
    64     def get_user(self, uid):
    65         return self.get(uid, None)
    66 
    67     def add_user(self, uid, **kw):
    68         u = User(**kw)
    69         self[uid] = u
     119        return [email.lower() for email in self.emails]
    70120
    71121    @property
    72122    def sports(self):
    73         catalog = get_catalog(self)
    74         return [s for s in catalog['sport']._fwd_index]
     123        return [s for s in self.catalog['sport']._fwd_index]
    75124
    76125    @property
  • ow/models/user.py

    r929097a r1d92bf2  
    2323    def __init__(self, **kw):
    2424        self.uid = kw.get('uid', uuid1())
     25        self.nickname = kw.get('nickname', '')
    2526        self.firstname = kw.get('firstname', '')
    2627        self.lastname = kw.get('lastname', '')
  • ow/schemas/user.py

    r929097a r1d92bf2  
    1919
    2020
    21 class UniqueUsername(validators.UnicodeString):
     21class UniqueNickname(validators.UnicodeString):
    2222    messages = {
    23         "name_exists": _('Another user is already registered with the '
    24                          'username %(name)s')
     23        "name_exists": _('Another user is already using the nickname %(name)s')
    2524    }
    2625
    2726    def _validate_python(self, value, state):
    28         super(UniqueUsername, self)._validate_python(value, state)
     27        super(UniqueNickname, self)._validate_python(value, state)
    2928        if value.lower() in state.names:
    3029            raise validators.Invalid(
     
    5150    allow_extra_fields = True
    5251    filter_extra_fields = True
    53     uid = validators.UnicodeString()
     52    email = UniqueEmail(not_empty=True)
     53    nickname = UniqueNickname(if_missing='')
    5454    firstname = validators.UnicodeString()
    5555    lastname = validators.UnicodeString()
    56     email = validators.Email(not_empty=True)
    5756    group = validators.UnicodeString(if_missing='')
    5857
     
    9291    allow_extra_fields = True
    9392    filter_extra_fields = True
    94     username = UniqueUsername(not_empty=True)
     93    nickname = UniqueNickname()
    9594    firstname = validators.UnicodeString(not_empty=True)
    9695    lastname = validators.UnicodeString(not_empty=True)
     
    108107    allow_extra_fields = True
    109108    filter_extra_fields = True
    110     username = UniqueUsername(not_empty=True)
     109    nickname = UniqueNickname()
    111110    email = UniqueEmail(not_empty=True)
  • ow/security.py

    r929097a r1d92bf2  
    99    allow other users to view workouts from a given user
    1010    """
    11     if uid in request.root.all_usernames():
    12         return [uid]
     11    user = request.root.get_user_by_uid(str(uid))
     12    if user is not None:
     13        return [str(user.uid)]
    1314    return []
  • ow/templates/add_user.pt

    r929097a r1d92bf2  
    2222
    2323      <fieldset>
    24         ${form.errorlist('uid')}
    25         <label for="email" i18n:translate="">User ID:</label>
    26         ${form.text('uid')}
    2724        ${form.errorlist('email')}
    2825        <label for="email" i18n:translate="">E-Mail:</label>
    2926        ${form.text('email')}
     27        ${form.errorlist('nickname')}
     28        <label for="nickname" i18n:translate="">Nickname</label>
     29        ${form.text('nickname')}
    3030      </fieldset>
    3131
  • ow/templates/login.pt

    r929097a r1d92bf2  
    2424          <fieldset>
    2525            <div>
    26               <label for="username" i18n:translate="">Username</label>
    27               <input placeholder="Your username..." type="text" name="username"
     26              <label for="email" i18n:translate="">Email</label>
     27              <input placeholder="Your email..." type="text" name="email"
    2828                     value="" i18n:attributes="placeholder"
    29                      tal:attributes="value username">
     29                     tal:attributes="value email">
    3030            </div>
    3131            <div>
  • ow/templates/signup.pt

    r929097a r1d92bf2  
    1818          <fieldset>
    1919            <div>
    20               ${form.errorlist('username')}
    21               <label for="username" i18n:translate="">Username</label>
    22               <input placeholder="Choose a cool username" type="text"
    23                      name="username" value="" i18n:attributes="placeholder">
    24             </div>
    25 
    26             <div>
    2720              ${form.errorlist('email')}
    2821              <label for="email" i18n:translate="">Email</label>
     
    3023                     type="text" name="email"
    3124                     value="" i18n:attributes="placeholder">
     25            </div>
     26            <div>
     27              ${form.errorlist('nickname')}
     28              <label for="nickname" i18n:translate="">Nickname</label>
     29              <input placeholder="Choose a cool nickname" type="text"
     30                     name="nickname" value="" i18n:attributes="placeholder">
    3231            </div>
    3332            <div>
     
    5049                     type="password" i18n:attributes="placeholder">
    5150            </div>
    52 
    5351            <div>
    5452              ${form.errorlist('password_confirm')}
  • ow/tests/models/test_root.py

    r929097a r1d92bf2  
    11import json
     2from unittest.mock import Mock
    23from datetime import datetime, timedelta, timezone
    34
    45import pytest
     6from repoze.catalog.catalog import Catalog
    57
    68from ow.models.workout import Workout
     
    1214
    1315    @pytest.fixture
    14     def root(self):
     16    def john(self):
     17        user = User(firstname='John', lastname='Doe',
     18                    email='john.doe@example.net')
     19        user.password = 's3cr3t'
     20        return user
     21
     22    @pytest.fixture
     23    def root(self, john):
    1524        root = OpenWorkouts()
    16         root['john'] = User(firstname='John', lastname='Doe',
    17                             email='john.doe@example.net')
    18         root['john'].password = 's3cr3t'
     25        root.add_user(john)
    1926        workout = Workout(
    2027            start=datetime(2015, 6, 28, 12, 55, tzinfo=timezone.utc),
     
    2229            distance=30, sport='cycling'
    2330        )
    24         root['john'].add_workout(workout)
     31        john.add_workout(workout)
    2532        return root
    2633
    27     def test_all_usernames(self, root):
    28         # the method returns an OOBtree object
    29         assert [u for u in root.all_usernames()] == ['john']
     34    def test__init__(self, root):
     35        # a new OpenWorkouts instance has a catalog created automatically
     36        assert isinstance(root.catalog, Catalog)
     37        assert len(root.catalog) == 2
     38        assert 'email' in root.catalog
     39        assert 'sport' in root.catalog
    3040
    31     def test_lowercase_usernames(self, root):
    32         root.add_user(uid='Jack', firstname='Jack', lastname='Dumb',
    33                       email='jack.dumb@example.net')
    34         assert root.lowercase_usernames() == ['jack', 'john']
     41    def test_add_user_ok(self, root):
     42        assert len(root.users) == 1
     43        user = User(firstname='New', lastname='For Testing',
     44                    email='new.for.testing@example.net')
     45        root.add_user(user)
     46        assert len(root.users) == 2
     47        assert user in root.users
     48
     49    def test_add_user_invalid(self, root):
     50        assert len(root.users) == 1
     51        with pytest.raises(AttributeError):
     52            root.add_user('faked-user-object')
     53
     54    def test_del_user_ok(self, root, john):
     55        assert len(root.users) == 1
     56        root.del_user(john)
     57        assert len(root.users) == 0
     58
     59    def test_del_user_failure(self, root):
     60        assert len(root.users) == 1
     61        with pytest.raises(AttributeError):
     62            root.add_user('faked-user-object')
     63
     64    def test_get_user_by_uid(self, root, john):
     65        # first, get an user that does exist
     66        user = root.get_user_by_uid(str(john.uid))
     67        assert user == john
     68        # now, without converting first to str, works too
     69        user = root.get_user_by_uid(john.uid)
     70        assert user == john
     71        # now, something that is not there
     72        new_user = User(firstname='someone', lastname='else',
     73                        email='someone.else@example.net')
     74        user = root.get_user_by_uid(new_user.uid)
     75        assert user is None
     76        # now, something that is not an uid
     77        user = root.get_user_by_uid('faked-user-uid')
     78        assert user is None
     79
     80    def test_get_user_by_email(self, root, john):
     81        # first, get an user that does exist
     82        user = root.get_user_by_email(str(john.email))
     83        assert user == john
     84        # now, something that is not there
     85        new_user = User(firstname='someone', lastname='else',
     86                        email='someone.else@example.net')
     87        user = root.get_user_by_email(new_user.email)
     88        assert user is None
     89        # now, something that is not an email
     90        user = root.get_user_by_email('faked-user-email')
     91        assert user is None
     92        # passing in None
     93        user = root.get_user_by_email(None)
     94        assert user is None
     95        # passing in something that is not None or a string will break
     96        # the query code
     97        with pytest.raises(TypeError):
     98            user = root.get_user_by_email(False)
     99        with pytest.raises(TypeError):
     100            user = root.get_user_by_email(Mock())
     101
     102    def test_users(self, root, john):
     103        assert root.users == [john]
     104
     105    def test_all_nicknames(self, root, john):
     106        # the existing user has not a nickname, and empty nicknames are not
     107        # added to the list of nicknames
     108        assert root.all_nicknames == []
     109        # now set one
     110        john.nickname = 'MrJohn'
     111        assert root.all_nicknames == ['MrJohn']
     112
     113    def test_lowercase_nicknames(self, root, john):
     114        # the existing user has not a nickname
     115        assert root.lowercase_nicknames == []
     116        # now set one
     117        john.nickname = 'MrJohn'
     118        assert root.lowercase_nicknames == ['mrjohn']
    35119
    36120    def test_emails(self, root):
    37         assert root.emails() == ['john.doe@example.net']
     121        assert root.emails == ['john.doe@example.net']
    38122
    39123    def test_lowercase_emails(self, root):
    40         root.add_user(uid='Jack', firstname='Jack', lastname='Dumb',
    41                       email='Jack.Dumb@example.net')
    42         assert root.lowercase_emails() == ['jack.dumb@example.net',
    43                                            'john.doe@example.net']
     124        user = User(firstname='Jack', lastname='Dumb',
     125                    email='Jack.Dumb@example.net')
     126        root.add_user(user)
     127        assert root.lowercase_emails == ['john.doe@example.net',
     128                                         'jack.dumb@example.net']
    44129
    45     def test_users(self, root):
    46         # the method returns an OOBtree object
    47         assert [u for u in root.users()] == [root['john']]
    48 
    49     def test_get_user(self, root):
    50         assert root.get_user('john') == root['john']
    51         assert root.get_user('jack') is None
    52         with pytest.raises(TypeError):
    53             root.get_user()
    54 
    55     def test_add_user(self, root):
    56         assert len(root.users()) == 1
    57         root.add_user(uid='jack', firstname='Jack', lastname='Dumb',
    58                       email='jack.dumb@example.net')
    59         assert len(root.users()) == 2
    60         assert 'jack' in root.all_usernames()
    61 
    62     def test_sports(self, root):
     130    def test_sports(self, root, john):
    63131        assert root.sports == ['cycling']
    64132        workout = Workout(
     
    66134            duration=timedelta(minutes=60),
    67135            distance=10, sport='running')
    68         root['john'].add_workout(workout)
     136        john.add_workout(workout)
    69137        assert root.sports == ['cycling', 'running']
    70138
    71     def test_sports_json(self, root):
     139    def test_sports_json(self, root, john):
    72140        assert root.sports_json == json.dumps(["cycling"])
    73141        workout = Workout(
     
    75143            duration=timedelta(minutes=60),
    76144            distance=10, sport='running')
    77         root['john'].add_workout(workout)
     145        john.add_workout(workout)
    78146        assert root.sports_json == json.dumps(["cycling", "running"])
  • ow/tests/models/test_workout.py

    r929097a r1d92bf2  
    5656        assert expected == joe['1'].end
    5757        assert 250 == joe['1'].distance * 1000
    58 
    59     def test_add_user(self, root):
    60         root.add_user('fred', firstname=u'Fred', lastname=u'Flintstone')
    61         assert u'Fred Flintstone' == root['fred'].fullname
    6258
    6359    def test_workout_id(self, root):
  • ow/tests/schemas/test_user.py

    r929097a r1d92bf2  
    44
    55from ow.models.user import User
    6 from ow.schemas.user import PasswordMatch, UniqueUsername, UniqueEmail
     6from ow.schemas.user import PasswordMatch, UniqueNickname, UniqueEmail
    77
    88
     
    2727
    2828
    29 class TestUniqueUsername(object):
     29class TestUniqueNickname(object):
    3030
    3131    @pytest.fixture
     
    3535
    3636    def test_validate_python_exists(self, state):
    37         validator = UniqueUsername()
     37        validator = UniqueNickname()
    3838        with pytest.raises(Invalid):
    3939            validator._validate_python('test', state)
     
    4242
    4343    def test_validate_python_not_exists(self, state):
    44         validator = UniqueUsername()
     44        validator = UniqueNickname()
    4545        res = validator._validate_python('not-existing', state)
    4646        assert res is None
  • ow/tests/test_catalog.py

    r929097a r1d92bf2  
    5656        changes = update_indexes(catalog, indexes)
    5757        assert changes['added'] == ['newindex']
    58         assert changes['removed'] == ['sport']
     58        assert changes['removed'] == ['email', 'sport']
    5959
    6060    def test_update_indexes_empty(self, root):
     
    6363        changes = update_indexes(catalog, indexes)
    6464        assert changes['added'] == []
    65         assert changes['removed'] == ['sport']
     65        assert changes['removed'] == ['email', 'sport']
    6666
    6767    def test_install_catalog(self):
    6868        root = OpenWorkouts()
     69        assert isinstance(getattr(root, 'catalog', None), Catalog)
     70        del root.catalog
    6971        assert getattr(root, 'catalog', None) is None
    7072        install_catalog(root)
     
    7880    def test_get_catalog_not_existing_catalog(self):
    7981        root = OpenWorkouts()
     82        assert isinstance(getattr(root, 'catalog', None), Catalog)
     83        del root.catalog
    8084        assert getattr(root, 'catalog', None) is None
    8185        catalog = get_catalog(root)
  • ow/tests/test_security.py

    r929097a r1d92bf2  
    2222        request.root = root
    2323        # User does exist
    24         assert groupfinder('john', request) == ['john']
     24        assert groupfinder('john', request) == [str(root['john'].uid)]
    2525        # User does not exist
    2626        assert groupfinder('jack', request) == []
  • ow/tests/views/test_root.py

    r929097a r1d92bf2  
    2020
    2121    @pytest.fixture
    22     def root(self):
     22    def john(self):
     23        user = User(firstname='John', lastname='Doe',
     24                    email='john.doe@example.net')
     25        user.password = 's3cr3t'
     26        return user
     27
     28    @pytest.fixture
     29    def root(self, john):
    2330        root = OpenWorkouts()
    24         root['john'] = User(firstname='John', lastname='Doe',
    25                             email='john.doe@example.net')
    26         root['john'].password = 's3cr3t'
     31        root.add_user(john)
    2732        workout = Workout(
    2833            start=datetime(2015, 6, 28, 12, 55, tzinfo=timezone.utc),
     
    3035            distance=30, sport='cycling'
    3136        )
    32         root['john'].add_workout(workout)
     37        john.add_workout(workout)
    3338        return root
    3439
     
    5055        return request
    5156
    52     def test_user_list(self, get_request):
     57    def test_user_list(self, get_request, john):
    5358        request = get_request
    5459        response = user_list(request.root, request)
    55         assert list(response['users']) == [request.root['john']]
     60        assert list(response['users']) == [john]
    5661
    5762    def test_add_user_get(self, get_request):
     
    6671        response = add_user(request.root, request)
    6772        assert 'form' in response
    68         # All required fields (4) are marked in the form errors
     73        # All required fields (3) are marked in the form errors
    6974        # You can see which fields are required in the schema
    7075        # ow.schemas.user.UserAddSchema
    71         assert len(response['form'].form.errors) == 4
     76        errors = response['form'].form.errors
     77        assert len(errors) == 3
     78        assert 'email' in errors
     79        assert 'firstname' in errors
     80        assert 'lastname' in errors
    7281
    7382    def test_add_user_post_valid(self, post_request):
    7483        request = post_request
    75         request.POST['uid'] = 'addeduser'
     84        request.POST['nickname'] = 'addeduser'
    7685        request.POST['email'] = 'addeduser@example.net'
    7786        request.POST['firstname'] = 'added'
     
    8089        assert isinstance(response, HTTPFound)
    8190        assert response.location.endswith('/userlist')
    82         assert len(request.root.all_usernames()) == 2
    83         assert 'addeduser' in request.root.all_usernames()
     91        # 1 nick name, as the default user has no nickname
     92        assert len(request.root.all_nicknames) == 1
     93        assert 'addeduser' in request.root.all_nicknames
  • ow/tests/views/test_user.py

    r929097a r1d92bf2  
    2424
    2525    @pytest.fixture
    26     def root(self):
     26    def john(self):
     27        user = User(firstname='John', lastname='Doe',
     28                    email='john.doe@example.net')
     29        user.password = 's3cr3t'
     30        return user
     31
     32    @pytest.fixture
     33    def root(self, john):
    2734        root = OpenWorkouts()
    28         root['john'] = User(firstname='John', lastname='Doe',
    29                             email='john.doe@example.net')
    30         root['john'].password = 's3cr3t'
     35        root.add_user(john)
    3136        workout = Workout(
    3237            start=datetime(2015, 6, 28, 12, 55, tzinfo=timezone.utc),
     
    3439            distance=30
    3540        )
    36         root['john'].add_workout(workout)
     41        john.add_workout(workout)
    3742        return root
    3843
     
    4449
    4550    @pytest.fixture
    46     def profile_post_request(self, root):
     51    def profile_post_request(self, root, john):
    4752        """
    4853        This is a valid POST request to update an user profile.
    4954        Form will validate, but nothing will be really updated/changed.
    5055        """
    51         user = root['john']
     56        user = john
    5257        request = DummyRequest()
    5358        request.root = root
     
    9499        request.POST = MultiDict({
    95100            'submit': True,
    96             'username': 'JackBlack',
     101            'nickname': 'JackBlack',
    97102            'email': 'jack.black@example.net',
    98103            'firstname': 'Jack',
     
    134139        assert response.location == '/dashboard'
    135140
    136     def test_dashboard(self, dummy_request):
     141    def test_dashboard(self, dummy_request, john):
    137142        """
    138143        Renders the user dashboard
    139144        """
    140145        request = dummy_request
    141         user = request.root['john']
    142         response = user_views.dashboard(user, request)
     146        response = user_views.dashboard(john, request)
    143147        assert response == {}
    144148
    145     def test_profile(self, dummy_request):
     149    def test_profile(self, dummy_request, john):
    146150        """
    147151        Renders the user profile page
    148152        """
    149153        request = dummy_request
    150         user = request.root['john']
    151         response = user_views.profile(user, request)
     154        response = user_views.profile(john, request)
    152155        assert response == {}
    153156
     
    159162        response = user_views.login(request.root, request)
    160163        assert response['message'] == ''
    161         assert response['username'] == ''
     164        assert response['email'] == ''
    162165        assert response['password'] == ''
    163166        assert response['redirect_url'] == request.resource_url(request.root)
    164167
    165     def test_login_get_return_to(self, dummy_request):
     168    def test_login_get_return_to(self, dummy_request, john):
    166169        """
    167170        GET request to access the login page, if there is a page set to where
     
    170173        """
    171174        request = dummy_request
    172         workout = request.root['john'].workouts()[0]
     175        workout = john.workouts()[0]
    173176        workout_url = request.resource_url(workout)
    174177        request.params['return_to'] = workout_url
     
    176179        assert response['redirect_url'] == workout_url
    177180
    178     def test_login_post_bad_username(self, dummy_request):
     181    def test_login_post_wrong_email(self, dummy_request):
    179182        request = dummy_request
    180183        request.method = 'POST'
    181184        request.POST['submit'] = True
    182         request.POST['username'] = 'jack'
     185        request.POST['email'] = 'jack@example.net'
    183186        response = user_views.login(request.root, request)
    184         assert response['message'] == u'Bad username'
    185 
    186     def test_login_post_bad_password(self, dummy_request):
     187        assert response['message'] == u'Wrong email address'
     188
     189    def test_login_post_wrong_password(self, dummy_request):
    187190        request = dummy_request
    188191        request.method = 'POST'
    189192        request.POST['submit'] = True
    190         request.POST['username'] = 'john'
     193        request.POST['email'] = 'john.doe@example.net'
    191194        request.POST['password'] = 'badpassword'
    192195        response = user_views.login(request.root, request)
    193         assert response['message'] == u'Bad password'
     196        assert response['message'] == u'Wrong password'
    194197
    195198    @patch('ow.views.user.remember')
     
    198201        request.method = 'POST'
    199202        request.POST['submit'] = True
    200         request.POST['username'] = 'john'
     203        request.POST['email'] = 'john.doe@example.net'
    201204        request.POST['password'] = 's3cr3t'
    202205        response = user_views.login(request.root, request)
     
    216219
    217220    @pytest.mark.parametrize('extension', extensions)
    218     def test_profile_picture(self, extension, dummy_request):
     221    def test_profile_picture(self, extension, dummy_request, john):
    219222        """
    220223        GET request to get the profile picture of an user.
     
    222225        request = dummy_request
    223226        # Get the user
    224         user = request.root['john']
     227        user = john
    225228        # Get the path to the image, then open it and copy it to a new Blob
    226229        # object
     
    242245        assert response.content_type == 'image'
    243246
    244     def test_edit_profile_get(self, dummy_request):
     247    def test_edit_profile_get(self, dummy_request, john):
    245248        """
    246249        GET request to the edit profile page, returns the form ready to
     
    248251        """
    249252        request = dummy_request
    250         user = request.root['john']
     253        user = john
    251254        response = user_views.edit_profile(user, request)
    252255        assert isinstance(response['form'], OWFormRenderer)
     
    261264        assert response['form'].data['email'] == 'john.doe@example.net'
    262265
    263     def test_edit_profile_post_ok(self, profile_post_request):
     266    def test_edit_profile_post_ok(self, profile_post_request, john):
    264267        request = profile_post_request
    265         user = request.root['john']
     268        user = john
    266269        # Update the bio field
    267270        bio = 'Some text about this user'
     
    272275        assert user.bio == bio
    273276
    274     def test_edit_profile_post_missing_required(self, profile_post_request):
     277    def test_edit_profile_post_missing_required(
     278            self, profile_post_request, john):
    275279        request = profile_post_request
    276280        request.POST['email'] = ''
    277         user = request.root['john']
     281        user = john
    278282        response = user_views.edit_profile(user, request)
    279283        assert isinstance(response['form'], OWFormRenderer)
     
    284288        assert response['form'].errors_for('email') == [error]
    285289
    286     def test_change_password_get(self, dummy_request):
    287         request = dummy_request
    288         user = request.root['john']
     290    def test_change_password_get(self, dummy_request, john):
     291        request = dummy_request
     292        user = john
    289293        response = user_views.change_password(user, request)
    290294        assert isinstance(response['form'], OWFormRenderer)
     
    292296        assert response['form'].errorlist() == ''
    293297
    294     def test_change_password_post_ok(self, passwd_post_request):
     298    def test_change_password_post_ok(self, passwd_post_request, john):
    295299        request = passwd_post_request
    296         user = request.root['john']
     300        user = john
    297301        request.POST['old_password'] = 's3cr3t'
    298302        request.POST['password'] = 'h1dd3n s3cr3t'
     
    305309        assert user.check_password('h1dd3n s3cr3t')
    306310
    307     def test_change_password_post_no_values(self, passwd_post_request):
     311    def test_change_password_post_no_values(self, passwd_post_request, john):
    308312        request = passwd_post_request
    309         user = request.root['john']
     313        user = john
    310314        response = user_views.change_password(user, request)
    311315        assert isinstance(response['form'], OWFormRenderer)
     
    322326        assert user.check_password('s3cr3t')
    323327
    324     def test_change_password_post_bad_old_password(self, passwd_post_request):
     328    def test_change_password_post_bad_old_password(
     329            self, passwd_post_request, john):
    325330        request = passwd_post_request
    326         user = request.root['john']
     331        user = john
    327332        request.POST['old_password'] = 'FAIL PASSWORD'
    328333        request.POST['password'] = 'h1dd3n s3cr3t'
     
    338343        assert not user.check_password('h1dd3n s3cr3t')
    339344
    340     def test_change_password_post_password_mismatch(self, passwd_post_request):
     345    def test_change_password_post_password_mismatch(
     346            self, passwd_post_request, john):
    341347        request = passwd_post_request
    342         user = request.root['john']
     348        user = john
    343349        request.POST['old_password'] = 's3cr3t'
    344350        request.POST['password'] = 'h1dd3n s3cr3ts'
     
    363369    def test_signup_post_ok(self, signup_post_request):
    364370        request = signup_post_request
    365         assert 'JackBlack' not in request.root.all_usernames()
     371        assert 'jack.black@example.net' not in request.root.emails
     372        assert 'JackBlack' not in request.root.all_nicknames
    366373        response = user_views.signup(request.root, request)
    367374        assert isinstance(response, HTTPFound)
    368375        assert response.location == request.resource_url(request.root)
    369         assert 'JackBlack' in request.root.all_usernames()
     376        assert 'jack.black@example.net' in request.root.emails
     377        assert 'JackBlack' in request.root.all_nicknames
    370378
    371379    def test_signup_missing_required(self, signup_post_request):
    372380        request = signup_post_request
    373381        request.POST['email'] = ''
    374         assert 'JackBlack' not in request.root.all_usernames()
     382        assert 'jack.black@example.net' not in request.root.emails
     383        assert 'JackBlack' not in request.root.all_nicknames
    375384        response = user_views.signup(request.root, request)
    376385        assert isinstance(response['form'], OWFormRenderer)
     
    382391        assert errorlist == html_error
    383392        assert response['form'].errors_for('email') == [error]
    384         assert 'JackBlack' not in request.root.all_usernames()
    385 
    386     def test_signup_existing_username(self, signup_post_request):
     393        assert 'jack.black@example.net' not in request.root.emails
     394        assert 'JackBlack' not in request.root.all_nicknames
     395
     396    def test_signup_existing_nickname(self, signup_post_request, john):
    387397        request = signup_post_request
    388         request.POST['username'] = 'john'
    389         assert 'JackBlack' not in request.root.all_usernames()
     398        # assign john a nickname first
     399        john.nickname = 'john'
     400        # now set it for the POST request
     401        request.POST['nickname'] = 'john'
     402        # check jack is not there yet
     403        assert 'jack.black@example.net' not in request.root.emails
     404        assert 'JackBlack' not in request.root.all_nicknames
     405        # now signup as jack, but trying to set the nickname 'john'
    390406        response = user_views.signup(request.root, request)
    391407        assert isinstance(response['form'], OWFormRenderer)
    392         error = u'Another user is already registered with the username john'
     408        error = u'Another user is already using the nickname john'
    393409        html_error = '<ul class="error">'
    394410        html_error += '<li>' + error + '</li>'
     
    396412        errorlist = response['form'].errorlist().replace('\n', '')
    397413        assert errorlist == html_error
    398         assert response['form'].errors_for('username') == [error]
    399         assert 'JackBlack' not in request.root.all_usernames()
     414        assert response['form'].errors_for('nickname') == [error]
     415        # all the errors, and jack is not there
     416        assert 'jack.black@example.net' not in request.root.emails
     417        assert 'JackBlack' not in request.root.all_nicknames
    400418
    401419    def test_signup_existing_email(self, signup_post_request):
    402420        request = signup_post_request
    403421        request.POST['email'] = 'john.doe@example.net'
    404         assert 'JackBlack' not in request.root.all_usernames()
     422        assert 'jack.black@example.net' not in request.root.emails
     423        assert 'JackBlack' not in request.root.all_nicknames
    405424        response = user_views.signup(request.root, request)
    406425        assert isinstance(response['form'], OWFormRenderer)
     
    413432        assert errorlist == html_error
    414433        assert response['form'].errors_for('email') == [error]
    415         assert 'JackBlack' not in request.root.all_usernames()
     434        assert 'jack.black@example.net' not in request.root.emails
     435        assert 'JackBlack' not in request.root.all_nicknames
  • ow/views/root.py

    r929097a r1d92bf2  
    11from pyramid.view import view_config
    2 from pyramid_simpleform import Form
     2from pyramid_simpleform import Form, State
    33from pyramid_simpleform.renderers import FormRenderer
    44from pyramid.httpexceptions import HTTPFound
     
    1818    Show a list of all the users to admins
    1919    """
    20     users = context.users()
     20    users = context.users
    2121    return {'users': users}
    2222
     
    3131    Form to add a user
    3232    """
    33     form = Form(request, schema=UserAddSchema())
     33    state = State(emails=context.lowercase_emails,
     34                  names=context.lowercase_nicknames)
     35
     36    form = Form(request, schema=UserAddSchema(), state=state)
    3437
    3538    if 'submit' in request.POST and form.validate():
    36         uid = request.POST['uid']
    37         user = form.bind(User(), exclude=['uid'])
    38         context[uid] = user
     39        user = form.bind(User())
     40        context[str(user.uid)] = user
    3941        return HTTPFound(location=request.resource_url(context, 'userlist'))
    4042
  • ow/views/user.py

    r929097a r1d92bf2  
    2525    """
    2626    if request.authenticated_userid:
    27         user = request.root.get_user(request.authenticated_userid)
    28         return HTTPFound(location=request.resource_url(user))
     27        user = request.root.get_user_by_uid(request.authenticated_userid)
     28        if user:
     29            return HTTPFound(location=request.resource_url(user))
     30        else:
     31            # an authenticated user session, for an user that does not exist
     32            # anymore, logout!
     33            return HTTPFound(location=request.resource_url(context, 'logout'))
    2934    return HTTPFound(location=request.resource_url(context, 'login'))
    3035
     
    3641def login(context, request):
    3742    message = ''
    38     username = ''
     43    email = ''
    3944    password = ''
    4045    return_to = request.params.get('return_to')
     
    4247
    4348    if 'submit' in request.POST:
    44         username = request.POST.get('username', None)
    45         if username in request.root.all_usernames():
    46             user = request.root[username]
     49        email = request.POST.get('email', None)
     50        user = context.get_user_by_email(email)
     51        if user:
    4752            password = request.POST.get('password', None)
    4853            if password is not None and user.check_password(password):
    49                 headers = remember(request, username)
     54                headers = remember(request, str(user.uid))
     55                redirect_url = return_to or request.resource_url(user)
    5056                return HTTPFound(location=redirect_url, headers=headers)
    5157            else:
    52                 message = u'Bad password'
     58                message = _('Wrong password')
    5359        else:
    54             message = u'Bad username'
     60            message = _('Wrong email address')
    5561
    5662    return {
    5763        'message': message,
    58         'username': username,
     64        'email': email,
    5965        'password': password,
    6066        'redirect_url': redirect_url
     
    7379    renderer='ow:templates/signup.pt')
    7480def signup(context, request):
    75     state = State(emails=context.lowercase_emails(),
    76                   names=context.lowercase_usernames())
     81    state = State(emails=context.lowercase_emails,
     82                  names=context.lowercase_nicknames)
    7783    form = Form(request, schema=SignUpSchema(), state=state)
    7884
    7985    if 'submit' in request.POST and form.validate():
    80         username = request.POST['username']
    81         user = form.bind(User(), exclude=['username', 'password_confirm'])
    82         context[username] = user
     86        user = form.bind(User(), exclude=['password_confirm'])
     87        context.add_user(user)
    8388        # Send to login
    8489        return HTTPFound(location=request.resource_url(context))
Note: See TracChangeset for help on using the changeset viewer.