source: main/waeup.kofa/trunk/src/waeup/kofa/authentication.txt @ 10521

Last change on this file since 10521 was 9308, checked in by Henrik Bettermann, 12 years ago

Add test for checking if also global roles are removed after deleting users. This test fails which means handle_account_removed must be extended.

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