Revision ddbaec88
Activation du cache des permissions (#577)
Modifie l'ordre des middlewares WSGI afin que les couches apportées par
Beaker (gestion du cache et gestion des sessions) soient disponibles
depuis les couches d'authentification/autorisations (repoze.who et
repoze.what).
Ceci permet d'activer réellement le cache des permissions au niveau du
plugin Kerberos (auparavant, la fonctionnalité était présent, mais
inutilisable car Beaker ne se trouvait pas dans la pile de couches).
Ce changeset remplace également le plugin d'authentification Kerberos
pour supprimer les méthodes responsables de l'identification et de
l'authentification de l'utilisateur par une unique méthode d'ajout de
méta-données (mdprovider).
De fait, le plugin sera correctement appelé avec le nouveau code pour la
pile d'authentification (afin de créer l'utilisateur dans Vigilo
correspondant à l'utilisateur externe).
Le plugin a été renommé (nom plus explicite/réaliste) et déplacé à cet
endroit : `vigilo.turbogears.repoze.plugins.mdldapsync`.
Ceci introduit une dépendance sur python-ldap dans vigilo.turbogears.
Ce changement centralise tout ce qui concerne repoze.wh(o|at) dans
vigilo.turbogears.repoze.
Le classifier a été modifié pour identifier spécifiquement les requêtes
authentifiée par un mécanisme externe (ex: Kerberos).
Les plugins ont vu leur nom changer radicalement et se trouvent
désormais chacun dans un fichier séparé (sqlauth, mduser, mdgroups).
Un nouveau module (d'identification) pour repoze.who a été ajouté dans
`vigilo.turbogears.repoze.plugins.externalid`. Ce module permet de
pré-authentifier un utilisateur à partir du contenu d'une session
Beaker. Il sera utilisé avec `mdldapsync` qui mémorise déjà l'identité
d'un utilisateur authentifié via un mécanisme externe dans la session
Beaker.
Les fichiers .ini des différentes IHM ont été mis à jour en conséquence.
Les méthodes liées à l'authentification dans les contrôleurs !TurboGears
ont été mises en commun dans `vigilo.turbogears.controllers.auth`.
Refs: #577.
Change-Id: Ia2d600622fd623e1a4474a4ea24fef3b3b503c12
Reviewed-on: https://vigilo-dev.si.c-s.fr/review/377
Tested-by: Build system <qa@vigilo-dev.si.c-s.fr>
Reviewed-by: Aurelien BOMPARD <aurelien.bompard@c-s.fr>
deployment/who.ini | ||
---|---|---|
5 | 5 |
|
6 | 6 |
[plugin:basicauth] |
7 | 7 |
use = repoze.who.plugins.basicauth:make_plugin |
8 |
realm=Vigilo
|
|
8 |
realm = Vigilo
|
|
9 | 9 |
|
10 | 10 |
[plugin:friendlyform] |
11 | 11 |
use = repoze.who.plugins.friendlyform:FriendlyFormPlugin |
... | ... | |
16 | 16 |
post_login_url = /post_login |
17 | 17 |
post_logout_url = /post_logout |
18 | 18 |
|
19 |
[plugin:sqlauth] |
|
20 |
use = vigilo.turbogears.repoze_plugins:auth_plugin |
|
21 |
|
|
22 | 19 |
[general] |
23 |
;request_classifier = repoze.who.classifiers:default_request_classifier |
|
24 |
request_classifier = vigilo.turbogears.repoze_plugins:vigilo_api_classifier |
|
20 |
request_classifier = vigilo.turbogears.repoze.classifier:vigilo_classifier |
|
25 | 21 |
challenge_decider = repoze.who.classifiers:default_challenge_decider |
26 | 22 |
|
27 | 23 |
[identifiers] |
... | ... | |
32 | 28 |
|
33 | 29 |
[authenticators] |
34 | 30 |
plugins = |
35 |
sqlauth
|
|
31 |
vigilo.turbogears.repoze.plugins.sqlauth:plugin
|
|
36 | 32 |
|
37 | 33 |
[challengers] |
38 | 34 |
plugins = |
... | ... | |
41 | 37 |
|
42 | 38 |
[mdproviders] |
43 | 39 |
plugins = |
44 |
vigilo.turbogears.repoze_plugins:md_plugin |
|
45 |
vigilo.turbogears.repoze_plugins:md_group_plugin |
|
40 |
vigilo.turbogears.repoze.plugins.mduser:plugin |
|
41 |
vigilo.turbogears.repoze.plugins.mdgroups:plugin |
test.ini | ||
---|---|---|
17 | 17 |
|
18 | 18 |
[app:main] |
19 | 19 |
sqlalchemy.url = sqlite:///:memory: |
20 |
;sqlalchemy.url = sqlite:////tmp/test-vigiboard.sqlite |
|
21 | 20 |
db_basename = vigilo_ |
22 |
use_kerberos = False |
|
23 | 21 |
vigiboard_items_per_page = 10 |
24 | 22 |
use = config:development.ini |
25 | 23 |
|
26 | 24 |
[app:main_without_authn] |
27 | 25 |
use = main |
28 | 26 |
skip_authentication = True |
29 |
|
vigiboard/config/middleware.py | ||
---|---|---|
26 | 26 |
from pkg_resources import resource_filename |
27 | 27 |
from paste.cascade import Cascade |
28 | 28 |
from paste.urlparser import StaticURLParser |
29 |
from vigilo.turbogears.repoze_who import make_middleware_with_config |
|
30 |
from logging import getLogger |
|
31 | 29 |
|
32 | 30 |
__all__ = ['make_app'] |
33 | 31 |
|
... | ... | |
56 | 54 |
""" |
57 | 55 |
app = make_base_app(global_conf, full_stack=full_stack, **app_conf) |
58 | 56 |
|
59 |
# Ajout du middleware d'authentification. |
|
60 |
app = make_middleware_with_config( |
|
61 |
app, global_conf, |
|
62 |
app_conf.get('auth.config', 'who.ini'), |
|
63 |
None, |
|
64 |
None, |
|
65 |
app_conf.get('skip_authentication') |
|
66 |
) |
|
67 |
# On force l'utilisation d'un logger nommé "auth" |
|
68 |
# pour la compatibilité avec TurboGears. |
|
69 |
app.logger = getLogger('auth') |
|
70 |
|
|
71 | 57 |
# On définit 2 middlewares pour fichiers statiques qui cherchent |
72 | 58 |
# les fichiers dans le thème actuellement chargé. |
73 | 59 |
# Le premier va les chercher dans le dossier des fichiers spécifiques |
vigiboard/controllers/root.py | ||
---|---|---|
48 | 48 |
from vigilo.models.tables.secondary_tables import EVENTSAGGREGATE_TABLE, \ |
49 | 49 |
USER_GROUP_TABLE, SUPITEM_GROUP_TABLE |
50 | 50 |
|
51 |
from vigilo.turbogears.controllers.auth import AuthController |
|
52 |
from vigilo.turbogears.controllers.error import ErrorController |
|
51 | 53 |
from vigilo.turbogears.controllers.autocomplete import AutoCompleteController |
52 | 54 |
from vigilo.turbogears.controllers.proxy import ProxyController |
53 | 55 |
from vigilo.turbogears.controllers.api.root import ApiRootController |
54 | 56 |
from vigilo.turbogears.helpers import get_current_user |
55 | 57 |
|
56 | 58 |
from vigiboard.controllers.vigiboardrequest import VigiboardRequest |
57 |
from vigiboard.controllers.vigiboard_controller import VigiboardRootController |
|
58 | 59 |
from vigiboard.controllers.feeds import FeedsController |
59 | 60 |
|
60 | 61 |
from vigiboard.widgets.edit_event import edit_event_status_options, \ |
... | ... | |
68 | 69 |
'date_to_timestamp') |
69 | 70 |
|
70 | 71 |
# pylint: disable-msg=R0201 |
71 |
class RootController(VigiboardRootController):
|
|
72 |
class RootController(AuthController):
|
|
72 | 73 |
""" |
73 | 74 |
Le controller général de vigiboard |
74 | 75 |
""" |
76 |
error = ErrorController() |
|
75 | 77 |
autocomplete = AutoCompleteController() |
76 | 78 |
nagios = ProxyController('nagios', '/nagios/', |
77 | 79 |
not_anonymous(l_('You need to be authenticated'))) |
... | ... | |
621 | 623 |
'address': request.remote_addr, |
622 | 624 |
'idevent': event.idcause, |
623 | 625 |
}) |
624 |
|
|
626 |
|
|
625 | 627 |
history = EventHistory( |
626 | 628 |
type_action=u"Acknowledgement change state", |
627 | 629 |
idevent=event.idcause, |
vigiboard/controllers/vigiboard_controller.py | ||
---|---|---|
1 |
# -*- coding: utf-8 -*- |
|
2 |
# vim:set expandtab tabstop=4 shiftwidth=4: |
|
3 |
################################################################################ |
|
4 |
# |
|
5 |
# Copyright (C) 2007-2011 CS-SI |
|
6 |
# |
|
7 |
# This program is free software; you can redistribute it and/or modify |
|
8 |
# it under the terms of the GNU General Public License version 2 as |
|
9 |
# published by the Free Software Foundation. |
|
10 |
# |
|
11 |
# This program is distributed in the hope that it will be useful, |
|
12 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
13 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
14 |
# GNU General Public License for more details. |
|
15 |
# |
|
16 |
# You should have received a copy of the GNU General Public License |
|
17 |
# along with this program; if not, write to the Free Software |
|
18 |
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
|
19 |
################################################################################ |
|
20 |
|
|
21 |
""" |
|
22 |
Controller for authentification |
|
23 |
""" |
|
24 |
import logging |
|
25 |
from tg import expose, flash, request, redirect |
|
26 |
from pylons.i18n import ugettext as _ |
|
27 |
|
|
28 |
from vigilo.turbogears.controllers import BaseController |
|
29 |
from vigilo.turbogears.controllers.error import ErrorController |
|
30 |
|
|
31 |
LOGGER = logging.getLogger(__name__) |
|
32 |
|
|
33 |
# pylint: disable-msg=R0201 |
|
34 |
class VigiboardRootController(BaseController): |
|
35 |
""" |
|
36 |
The root controller for the vigiboard application. |
|
37 |
|
|
38 |
All the other controllers and WSGI applications should be mounted on this |
|
39 |
controller. For example:: |
|
40 |
|
|
41 |
panel = ControlPanelController() |
|
42 |
another_app = AnotherWSGIApplication() |
|
43 |
|
|
44 |
Keep in mind that WSGI applications shouldn't be mounted directly: They |
|
45 |
must be wrapped around with :class:`tg.controllers.WSGIAppController`. |
|
46 |
""" |
|
47 |
|
|
48 |
error = ErrorController() |
|
49 |
|
|
50 |
@expose('login.html') |
|
51 |
def login(self, came_from='/'): |
|
52 |
"""Start the user login.""" |
|
53 |
login_counter = request.environ['repoze.who.logins'] |
|
54 |
if login_counter > 0: |
|
55 |
flash(_('Wrong credentials'), 'warning') |
|
56 |
return dict(page='login', login_counter=str(login_counter), |
|
57 |
came_from=came_from) |
|
58 |
|
|
59 |
@expose() |
|
60 |
def post_login(self, came_from='/'): |
|
61 |
""" |
|
62 |
Redirect the user to the initially requested page on successful |
|
63 |
authentication or redirect her back to the login page if login failed. |
|
64 |
""" |
|
65 |
if not request.identity: |
|
66 |
login_counter = request.environ['repoze.who.logins'] + 1 |
|
67 |
redirect('/login', came_from=came_from, __logins=login_counter) |
|
68 |
userid = request.identity['repoze.who.userid'] |
|
69 |
LOGGER.info(_('"%(username)s" logged in (from %(IP)s)') % { |
|
70 |
'username': userid, |
|
71 |
'IP': request.remote_addr, |
|
72 |
}) |
|
73 |
flash(_('Welcome back, %s!') % userid) |
|
74 |
redirect(came_from) |
|
75 |
|
|
76 |
@expose() |
|
77 |
def post_logout(self, came_from='/'): |
|
78 |
""" |
|
79 |
Redirect the user to the initially requested page on logout and say |
|
80 |
goodbye as well. |
|
81 |
""" |
|
82 |
LOGGER.info(_('Some user logged out (from %(IP)s)') % { |
|
83 |
'IP': request.remote_addr, |
|
84 |
}) |
|
85 |
flash(_('We hope to see you soon!')) |
|
86 |
redirect(came_from) |
vigiboard/tests/functional/test_group_selection_tree.py | ||
---|---|---|
51 | 51 |
json, {'items': [], 'groups': []} |
52 | 52 |
) |
53 | 53 |
|
54 |
def test_get_group_when_not_allowed(self): |
|
55 |
"""Récupération de l'étage de l'arbre sans les droits""" |
|
56 |
|
|
54 |
def test_get_group_anonymous(self): |
|
55 |
"""Récupération de l'étage de l'arbre en anonyme""" |
|
57 | 56 |
# Récupération du groupe utilisé lors de ce test. |
58 | 57 |
group2 = SupItemGroup.by_group_name(u'group2') |
59 | 58 |
|
... | ... | |
62 | 61 |
response = self.app.get('/get_groups?parent_id=%d' % group2.idgroup, |
63 | 62 |
status=401) |
64 | 63 |
|
64 |
def test_get_group_when_not_allowed(self): |
|
65 |
"""Récupération de l'étage de l'arbre sans les droits""" |
|
66 |
|
|
67 |
# Récupération du groupe utilisé lors de ce test. |
|
68 |
group2 = SupItemGroup.by_group_name(u'group2') |
|
69 |
|
|
65 | 70 |
# L'utilisateur est authentifié avec des permissions |
66 | 71 |
# restreintes. Il cherche à obtenir la liste des groupes fils |
67 | 72 |
# d'un groupe auquel il n'a pas accès, même indirectement. |
... | ... | |
93 | 98 |
# bien les groupes fils de ce groupe parent |
94 | 99 |
self.assertEqual( |
95 | 100 |
json, { |
96 |
'items': [],
|
|
101 |
'items': [], |
|
97 | 102 |
'groups': [ |
98 | 103 |
{'id': maingroup.idgroup, 'name': maingroup.name, 'type': 'group'} |
99 | 104 |
] |
... | ... | |
111 | 116 |
# bien les groupes fils de ce groupe parent |
112 | 117 |
self.assertEqual( |
113 | 118 |
json, { |
114 |
'items': [],
|
|
119 |
'items': [], |
|
115 | 120 |
'groups': [ |
116 | 121 |
{'id': maingroup.idgroup, 'name': maingroup.name, 'type': 'group'} |
117 | 122 |
] |
... | ... | |
127 | 132 |
# On s'assure que la liste retournée contient |
128 | 133 |
# bien les groupes fils de ce groupe parent. |
129 | 134 |
self.assertEqual(json, { |
130 |
'items': [],
|
|
135 |
'items': [], |
|
131 | 136 |
'groups': [ |
132 | 137 |
{'id': group1.idgroup, 'name': group1.name, 'type': 'group'}, |
133 | 138 |
{'id': group2.idgroup, 'name': group2.name, 'type': 'group'} |
... | ... | |
145 | 150 |
# On s'assure que la liste retournée contient bien ce groupe fils. |
146 | 151 |
self.assertEqual( |
147 | 152 |
json, { |
148 |
'items': [],
|
|
153 |
'items': [], |
|
149 | 154 |
'groups': [ |
150 | 155 |
{'id': group1.idgroup, 'name': group1.name, 'type': 'group'} |
151 | 156 |
] |
... | ... | |
162 | 167 |
# le groupe parent du groupe auquel il a accès. |
163 | 168 |
self.assertEqual( |
164 | 169 |
json, { |
165 |
'items': [],
|
|
170 |
'items': [], |
|
166 | 171 |
'groups': [ |
167 | 172 |
{'id': maingroup.idgroup, 'name': maingroup.name, 'type': 'group'} |
168 | 173 |
] |
... | ... | |
184 | 189 |
# On s'assure que la liste retournée contient bien le groupe racine. |
185 | 190 |
self.assertEqual( |
186 | 191 |
json, { |
187 |
'items': [],
|
|
192 |
'items': [], |
|
188 | 193 |
'groups': [ |
189 | 194 |
{'id': root.idgroup, 'name': root.name, 'type': 'group'} |
190 | 195 |
] |
... | ... | |
201 | 206 |
# groupe racine, auquel cet utilisateur a directement accès. |
202 | 207 |
self.assertEqual( |
203 | 208 |
json, { |
204 |
'items': [],
|
|
209 |
'items': [], |
|
205 | 210 |
'groups': [ |
206 | 211 |
{'id': root.idgroup, 'name': root.name, 'type': 'group'} |
207 | 212 |
] |
... | ... | |
218 | 223 |
# groupe racine, auquel cet utilisateur a indirectement accès. |
219 | 224 |
self.assertEqual( |
220 | 225 |
json, { |
221 |
'items': [],
|
|
226 |
'items': [], |
|
222 | 227 |
'groups': [ |
223 | 228 |
{'id': root.idgroup, 'name': root.name, 'type': 'group'} |
224 | 229 |
] |
225 | 230 |
} |
226 | 231 |
) |
227 | 232 |
|
233 |
def test_get_root_group_anonymous(self): |
|
234 |
"""Récupération des groupes racines de l'arbre en anonyme""" |
|
235 |
# L'utilisateur n'est pas authentifié, et cherche |
|
236 |
# à obtenir la liste des groupes racines de l'arbre. |
|
237 |
response = self.app.get('/get_groups', status=401) |
|
238 |
|
|
239 |
|
|
228 | 240 |
def test_get_root_group_when_not_allowed(self): |
229 | 241 |
"""Récupération des groupes racines de l'arbre sans les droits""" |
230 |
|
|
231 | 242 |
# Récupération du groupe utilisé lors de ce test. |
232 | 243 |
root = SupItemGroup.by_group_name(u'root') |
233 | 244 |
|
234 |
# L'utilisateur n'est pas authentifié, et cherche |
|
235 |
# à obtenir la liste des groupes racines de l'arbre. |
|
236 |
response = self.app.get('/get_groups', status=401) |
|
237 |
|
|
238 | 245 |
# Création d'un nouvel utilisateur et d'un nouveau groupe |
239 | 246 |
usergroup = UserGroup(group_name=u'new_users') |
240 | 247 |
vigiboard_perm = Permission.by_permission_name(u'vigiboard-access') |
... | ... | |
246 | 253 |
) |
247 | 254 |
user.usergroups.append(usergroup) |
248 | 255 |
DBSession.add(user) |
256 |
DBSession.flush() |
|
257 |
transaction.commit() |
|
249 | 258 |
|
250 | 259 |
# L'utilisateur est authentifié mais n'a aucun accès. Il |
251 | 260 |
# cherche à obtenir la liste des groupes racines de l'arbre. |
... | ... | |
256 | 265 |
# On s'assure que la liste retournée est bien vide. |
257 | 266 |
self.assertEqual( |
258 | 267 |
json, { |
259 |
'items': [],
|
|
268 |
'items': [], |
|
260 | 269 |
'groups': [] |
261 | 270 |
} |
262 | 271 |
) |
263 |
|
who.ini | ||
---|---|---|
5 | 5 |
|
6 | 6 |
[plugin:basicauth] |
7 | 7 |
use = repoze.who.plugins.basicauth:make_plugin |
8 |
realm=Vigilo
|
|
8 |
realm = Vigilo
|
|
9 | 9 |
|
10 | 10 |
[plugin:friendlyform] |
11 | 11 |
use = repoze.who.plugins.friendlyform:FriendlyFormPlugin |
... | ... | |
16 | 16 |
post_login_url = /post_login |
17 | 17 |
post_logout_url = /post_logout |
18 | 18 |
|
19 |
[plugin:sqlauth] |
|
20 |
use = vigilo.turbogears.repoze_plugins:auth_plugin |
|
21 |
|
|
22 | 19 |
;[plugin:kerberos] |
23 | 20 |
;use = repoze.who.plugins.vigilo.kerberos:VigiloKerberosAuthenticator |
24 | 21 |
;ldap_url = ldap://ldap.example.com |
... | ... | |
30 | 27 |
;bindpw = mybindpassword |
31 | 28 |
|
32 | 29 |
[general] |
33 |
request_classifier = vigilo.turbogears.repoze_plugins:vigilo_api_classifier
|
|
30 |
request_classifier = vigilo.turbogears.repoze.classifier:vigilo_classifier
|
|
34 | 31 |
challenge_decider = repoze.who.classifiers:default_challenge_decider |
35 | 32 |
|
36 | 33 |
[identifiers] |
... | ... | |
41 | 38 |
|
42 | 39 |
[authenticators] |
43 | 40 |
plugins = |
44 |
sqlauth
|
|
41 |
vigilo.turbogears.repoze.plugins.sqlauth:plugin
|
|
45 | 42 |
|
46 | 43 |
[challengers] |
47 | 44 |
plugins = |
... | ... | |
50 | 47 |
|
51 | 48 |
[mdproviders] |
52 | 49 |
plugins = |
53 |
vigilo.turbogears.repoze_plugins:md_plugin |
|
54 |
vigilo.turbogears.repoze_plugins:md_group_plugin |
|
50 |
vigilo.turbogears.repoze.plugins.mduser:plugin |
|
51 |
vigilo.turbogears.repoze.plugins.mdgroups:plugin |
Also available in: Unified diff