source: main/waeup.sirp/trunk/src/waeup/sirp/authentication.txt @ 7172

Last change on this file since 7172 was 7172, checked in by Henrik Bettermann, 13 years ago

Rename UserContainer? to UsersContainer? to be in line with ApplicantsContainer?, StudentsContainer?, HostelsCointainer?, PaymentsContainer?. In other words, a userscontainer contains users and it's not the container of a user. This is not really necessary in terms of English grammar but it helps to confuse container types.

File size: 6.2 KB
RevLine 
[4085]1WAeUP portal authentication
2***************************
3
4We need to protect most pieces of our portals from unauthenticated
5access.
6
7Therefore users have to login to access main functionality and they
8are able to log out afterwards.
9
10Before we can check access we have to create an app:
11
[6180]12  >>> from zope.component.hooks import setSite # only needed in tests
[4921]13  >>> from waeup.sirp.app import University
[4085]14  >>> root = getRootFolder()
15  >>> u = University()
16  >>> root['app'] = u
[6180]17  >>> setSite(root['app'])                     # only needed in tests
[4085]18
19To make sure, we can 'watch' pages, we first have to initialize our
20test browser:
21
22  >>> from zope.testbrowser.testing import Browser
23  >>> browser = Browser()
24  >>> browser.handleErrors = False
25
[4092]26Creating users (principals)
27===========================
[4085]28
[4092]29Before we can login, we have to provide a user (``principal`` in Zope
[4093]30terms) with a password (and optional a title or description):
[4092]31
[4744]32  >>> root['app']['users'].addUser('bob', 'bobsecret',
[4093]33  ...                           title='Bob', description='A sample user')
[4092]34
35We can also add complete `Account` objects. An `Account` stores the
36user credentials and some metadata persistently:
37
[4921]38  >>> from waeup.sirp.authentication import Account
[4092]39  >>> alice = Account('alice', 'alicesecret')
[4744]40  >>> root['app']['users'].addAccount(alice)
[4092]41
[7172]42See ``users.txt`` for details about the UsersContainer we use here.
[4092]43
[6180]44Users and local roles
45=====================
[4093]46
[6180]47Accounts also hold infos about local roles assigned to a user. In the
[7163]48beginning, users have only the local owner role of their own account object:
[6180]49
50  >>> alice.getLocalRoles()
[7163]51  {'waeup.local.Owner': [<waeup.sirp.authentication.Account object at 0x...>]}
[6180]52
[7163]53We can tell an account, that Alice got some role for another object:
[6180]54
55  >>> chalet = object()
56  >>> root['app']['chalet'] = chalet
57  >>> alice.notifyLocalRoleChanged(chalet, 'BigBoss', granted=True)
58
59Now Alice is the Big Boss:
60
61  >>> alice.getLocalRoles()
62  {'BigBoss': [<object object at 0x...>]}
63
64When we do not want Alice to be the Big Boss we can tell that too:
65
66  >>> alice.notifyLocalRoleChanged(chalet, 'BigBoss', granted=False)
67  >>> alice.getLocalRoles()
[7163]68  {'waeup.local.Owner': [<waeup.sirp.authentication.Account object at 0x...>]}
[6180]69
70We can also use events to trigger such actions. This is recommended
71because we do not neccessarily know where Alice lives:
72
[7169]73  >>> from waeup.sirp.authentication import LocalRoleSetEvent
[6180]74  >>> from zope.event import notify
75  >>> notify(LocalRoleSetEvent(chalet, 'BigBoss', 'alice',
76  ...                          granted=True))
77  >>> alice.getLocalRoles()
78  {'BigBoss': [<object object at 0x...>]}
79
80When objects are deleted, local roles are also deleted
81semi-magically. This happens through event subscribers listening to
82IObjectRemovedEvents. The latters are naturally only fired when ZODB
83stored objects are removed. Furthermore this subscriber reads the
84internal local roles table.
85
86We create a faculty and grant Bob a local role:
87
88   >>> from zope.securitypolicy.interfaces import IPrincipalRoleManager
89   >>> from waeup.sirp.university.faculty import Faculty
90   >>> faculty = Faculty()
91   >>> root['app']['bobs_fac'] = faculty
92   >>> role_manager = IPrincipalRoleManager(faculty)
93   >>> role_manager.assignRoleToPrincipal(
94   ...    'waeup.PortalManager', 'bob')
95
96We notify the machinery about that fact:
97
98   >>> notify(LocalRoleSetEvent(faculty, 'waeup.PortalManager', 'bob',
99   ...                          granted=True))
100   >>> bob = root['app']['users']['bob']
101   >>> bob.getLocalRoles()
102   {'waeup.PortalManager': [<waeup.sirp...Faculty object at 0x...>]}
103
104When we delete the faculty from ZODB, also Bobs roles are modified:
105
106   >>> del root['app']['bobs_fac']
107   >>> bob.getLocalRoles()
[7163]108   {'waeup.local.Owner': [<waeup.sirp.authentication.Account object at 0x...>]}
[6180]109
[6202]110If one notifies the machinery of a local role removal for an object
111that cannot have local roles, this will cause no trouble:
112
113   >>> mycontext = None #object()
114   >>> notify(LocalRoleSetEvent(
115   ...   mycontext, 'waeup.PortalManager', 'bob', granted=False
116   ... )) is None
117   True
118
[6203]119When an account get deleted, also the local roles of the owner get
120removed. Let's setup a local role for `alice`:
121
122   >>> faculty = Faculty()
123   >>> root['app']['alice_fac'] = faculty
124   >>> role_manager = IPrincipalRoleManager(faculty)
125   >>> role_manager.assignRoleToPrincipal(
126   ...    'waeup.PortalManager', 'alice')
127   >>> notify(LocalRoleSetEvent(faculty, 'waeup.PortalManager', 'alice',
128   ...                          granted=True))
129
130The local role is set now:
131
132   >>> from zope.securitypolicy.interfaces import IPrincipalRoleMap
133   >>> IPrincipalRoleMap(faculty).getPrincipalsAndRoles()
134   [('waeup.PortalManager', 'alice', PermissionSetting: Allow)]
135
136But when we delete Alices account from ZODB:
137
138   >>> del root['app']['users']['alice']
139   >>> IPrincipalRoleMap(faculty).getPrincipalsAndRoles()
140   []
141
142the local role has gone.
143
144
[4092]145Logging in via side bar
146=======================
147
[4085]148We can access the front page without restrictions:
149
150  >>> browser.open('http://localhost/app')
151  >>> print browser.headers['Status']
152  200 Ok
153
[5404]154We have to go to one of the login pages first:
[4086]155
[6609]156  >>> browser.open('http://localhost/app')
[6685]157  >>> browser.getLink('Login').click()
[5404]158  >>> print browser.headers['Status']
159  200 Ok
160
161There is a login form on tis page:
162
[4086]163  >>> 'form.login' in browser.contents
164  True
165
[4093]166  >>> 'form.logout' in browser.contents
167  False
168
[4086]169We use this form:
170
[4093]171  >>> browser.getControl(name='form.login').value = 'bob'
[6609]172  >>> browser.getControl(name='form.password').value = 'invalidpw'
173  >>> browser.getControl('Login').click()
174  >>> 'You entered wrong credentials' in browser.contents
175  True
176
177  >>> browser.getControl(name='form.login').value = 'bob'
[4093]178  >>> browser.getControl(name='form.password').value = 'bobsecret'
[4086]179  >>> browser.getControl('Login').click()
[4092]180
[4093]181Now the login form is gone. Instead we have the opportunity to logout:
182
183  >>> 'form.login' in browser.contents
184  False
185
[4617]186  >>> logout = browser.getLink('Logout')
187  >>> logout
188  <Link text='Logout' url='http://localhost/app/@@logout'>
[4093]189
[4613]190The user title is also displayed in the sidebar:
[4093]191
[4613]192  >>> 'Bob' in browser.contents
[4094]193  True
[4093]194
[4094]195We can also log out afterwards:
196
[4617]197  >>> logout.click()
[5404]198  >>> print browser.contents
199  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"...
[6691]200  ...Login
[5404]201  ...
[4094]202
Note: See TracBrowser for help on using the repository browser.