3 # Copyright (C) 2013 Simo Sorce <simo@redhat.com>
5 # see file 'COPYING' for use and warranty information
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
20 from ipsilon.util.log import Log
21 from ipsilon.util.page import Page
22 from ipsilon.util.user import UserSession
23 from ipsilon.util.plugin import PluginLoader, PluginObject
24 from ipsilon.util.plugin import PluginInstaller
25 from ipsilon.info.common import Info
26 from ipsilon.util.cookies import SecureCookie
30 USERNAME_COOKIE = 'ipsilon_default_username'
33 class LoginManagerBase(PluginObject, Log):
36 super(LoginManagerBase, self).__init__()
38 self.next_login = None
41 def redirect_to_path(self, path):
42 base = cherrypy.config.get('base.mount', "")
43 raise cherrypy.HTTPRedirect('%s/login/%s' % (base, path))
45 def auth_successful(self, username, auth_type=None, userdata=None):
46 # save ref before calling UserSession login() as it
47 # may regenerate the session
48 session = UserSession()
49 ref = session.get_data('login', 'Return')
51 ref = cherrypy.config.get('base.mount', "") + '/'
54 userattrs = self.info.get_user_attrs(username)
56 userdata.update(userattrs or {})
59 self.debug("User %s attributes: %s" % (username, repr(userdata)))
63 userdata.update({'auth_type': auth_type})
65 userdata = {'auth_type': auth_type}
67 session.login(username, userdata)
69 # save username into a cookie if parent was form base auth
70 if auth_type == 'password':
71 cookie = SecureCookie(USERNAME_COOKIE, username)
73 cookie.maxage = 1296000
76 raise cherrypy.HTTPRedirect(ref)
78 def auth_failed(self):
79 # try with next module
81 return self.redirect_to_path(self.next_login.path)
83 # return to the caller if any
84 session = UserSession()
85 ref = session.get_data('login', 'Return')
87 # otherwise destroy session and return error
90 raise cherrypy.HTTPError(401)
92 raise cherrypy.HTTPRedirect(ref)
94 def get_tree(self, site):
95 raise NotImplementedError
97 def enable(self, site):
98 plugins = site[FACILITY]
99 if self in plugins['enabled']:
103 if self.name in plugins['config']:
104 self.set_config(plugins['config'][self.name])
106 # and add self to the root
107 root = plugins['root']
108 root.add_subtree(self.name, self.get_tree(site))
110 # finally add self in login chain
112 for prev_obj in plugins['enabled']:
113 if prev_obj.next_login:
116 while prev_obj.next_login:
117 prev_obj = prev_obj.next_login
118 prev_obj.next_login = self
119 if not root.first_login:
120 root.first_login = self
122 plugins['enabled'].append(self)
123 self._debug('Login plugin enabled: %s' % self.name)
125 # Get handle of the info plugin
126 self.info = root.info
128 def disable(self, site):
129 plugins = site[FACILITY]
130 if self not in plugins['enabled']:
133 # remove self from chain
134 root = plugins['root']
135 if root.first_login == self:
136 root.first_login = self.next_login
137 elif root.first_login:
138 prev_obj = root.first_login
139 while prev_obj.next_login != self:
140 prev_obj = prev_obj.next_login
142 prev_obj.next_login = self.next_login
143 self.next_login = None
145 plugins['enabled'].remove(self)
146 self._debug('Login plugin disabled: %s' % self.name)
149 class LoginPageBase(Page):
151 def __init__(self, site, mgr):
152 super(LoginPageBase, self).__init__(site)
155 def root(self, *args, **kwargs):
156 raise cherrypy.HTTPError(500)
159 class LoginFormBase(LoginPageBase):
161 def __init__(self, site, mgr, page, template=None):
162 super(LoginFormBase, self).__init__(site, mgr)
164 self.formtemplate = template or 'login/form.html'
166 def GET(self, *args, **kwargs):
167 context = self.create_tmpl_context()
168 # pylint: disable=star-args
169 return self._template(self.formtemplate, **context)
171 def root(self, *args, **kwargs):
172 op = getattr(self, cherrypy.request.method, self.GET)
174 return op(*args, **kwargs)
176 def create_tmpl_context(self, **kwargs):
178 if self.lm.next_login is not None:
179 next_url = self.lm.next_login.path
181 cookie = SecureCookie(USERNAME_COOKIE)
183 username = cookie.value
189 "action": '%s/%s' % (self.basepath, self.formpage),
190 "service_name": self.lm.service_name,
191 "username_text": self.lm.username_text,
192 "password_text": self.lm.password_text,
193 "description": self.lm.help_text,
194 "next_url": next_url,
195 "username": username,
197 context.update(kwargs)
201 FACILITY = 'login_config'
206 def __init__(self, *args, **kwargs):
207 super(Login, self).__init__(*args, **kwargs)
208 self.first_login = None
209 self.info = Info(self._site)
211 loader = PluginLoader(Login, FACILITY, 'LoginManager')
212 self._site[FACILITY] = loader.get_plugin_data()
213 plugins = self._site[FACILITY]
215 available = plugins['available'].keys()
216 self._debug('Available login managers: %s' % str(available))
218 plugins['root'] = self
219 for item in plugins['whitelist']:
220 self._debug('Login plugin in whitelist: %s' % item)
221 if item not in plugins['available']:
223 plugins['available'][item].enable(self._site)
225 def add_subtree(self, name, page):
226 self.__dict__[name] = page
228 def root(self, *args, **kwargs):
230 raise cherrypy.HTTPRedirect('%s/login/%s' %
232 self.first_login.path))
233 return self._template('login/index.html', title='Login')
238 def root(self, *args, **kwargs):
239 UserSession().logout(self.user)
240 return self._template('logout.html', title='Logout')
243 class LoginMgrsInstall(object):
246 pi = PluginInstaller(LoginMgrsInstall)
247 self.plugins = pi.get_plugins()