diff options
| author | Petr Vobornik <pvoborni@redhat.com> | 2014-01-23 15:00:51 +0100 |
|---|---|---|
| committer | Petr Vobornik <pvoborni@redhat.com> | 2014-01-24 19:07:05 +0100 |
| commit | efaed49f0c8069e3c3ddaf32be7bb8d26c8aae4c (patch) | |
| tree | 91047b2a197bab1d613aab5028a6399ec67e3ff1 /ipsilon/login | |
| parent | 96891701df4f1a3c2416663fcc84dde3de3e6bd7 (diff) | |
| download | ipsilon-efaed49f0c8069e3c3ddaf32be7bb8d26c8aae4c.tar.gz ipsilon-efaed49f0c8069e3c3ddaf32be7bb8d26c8aae4c.tar.xz ipsilon-efaed49f0c8069e3c3ddaf32be7bb8d26c8aae4c.zip | |
Rename src package to ipsilon
Diffstat (limited to 'ipsilon/login')
| -rw-r--r-- | ipsilon/login/__init__.py | 0 | ||||
| -rwxr-xr-x | ipsilon/login/authkrb.py | 78 | ||||
| -rwxr-xr-x | ipsilon/login/authpam.py | 112 | ||||
| -rwxr-xr-x | ipsilon/login/common.py | 158 |
4 files changed, 348 insertions, 0 deletions
diff --git a/ipsilon/login/__init__.py b/ipsilon/login/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/ipsilon/login/__init__.py diff --git a/ipsilon/login/authkrb.py b/ipsilon/login/authkrb.py new file mode 100755 index 0000000..867da0c --- /dev/null +++ b/ipsilon/login/authkrb.py @@ -0,0 +1,78 @@ +#!/usr/bin/python +# +# Copyright (C) 2014 Simo Sorce <simo@redhat.com> +# +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +from login import common +from util import page +import cherrypy + +class Krb(common.LoginPageBase): + + def root(self, *args, **kwargs): + # Someone typed manually or a robot is walking th tree. + # Redirect to default page + return self.lm.redirect_to_path(self.lm.path) + + +class KrbAuth(common.LoginPageBase): + + def root(self, *args, **kwargs): + # If we can get here, we must be authenticated and remote_user + # was set. Check the session has a use set already or error. + if self.user and self.user.name: + return self.lm.auth_successful(self.user.name) + else: + return self.lm.auth_failed() + + +class KrbError(common.LoginPageBase): + + def root(self, *args, **kwargs): + cherrypy.log.error('REQUEST: %s' % cherrypy.request.headers) + # If we have no negotiate header return whatever mod_auth_kerb + # generated and wait for the next request + + if not 'WWW-Authenticate' in cherrypy.request.headers: + cherrypy.response.status = 401 + + if self.lm.next_login: + return self.lm.next_login.page.root(*args, **kwargs) + + conturl = '%s/login' % self.basepath + return self._template('login/krb.html', + title='Kerberos Login', + cont=conturl) + + # If we get here, negotiate failed + return self.lm.auth_failed() + +class LoginManager(common.LoginManagerBase): + + def __init__(self, *args, **kwargs): + super(LoginManager, self).__init__(*args, **kwargs) + self.name = 'krb' + self.path = 'krb/negotiate' + self.description = """ +Kereros Negotiate authentication plugin. Relies on the mod_auth_kerb apache +plugin for actual authentication. """ + + def get_tree(self, site): + self.page = Krb(site, self) + self.page.negotiate = KrbAuth(site, self) + self.page.unauthorized = KrbError(site, self) + return self.page diff --git a/ipsilon/login/authpam.py b/ipsilon/login/authpam.py new file mode 100755 index 0000000..d11731d --- /dev/null +++ b/ipsilon/login/authpam.py @@ -0,0 +1,112 @@ +#!/usr/bin/python +# +# Copyright (C) 2013 Simo Sorce <simo@redhat.com> +# +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +from login import common +from util import page +import cherrypy +import pam + +class Pam(common.LoginPageBase): + + def _authenticate(self, username, password): + if self.lm.service_name: + ok = pam.authenticate(username, password, self.lm.service_name) + else: + ok = pam.authenticate(username, password) + + if ok: + cherrypy.log("User %s successfully authenticated." % username) + return username + + cherrypy.log("User %s failed authentication." % username) + return None + + def GET(self, *args, **kwargs): + return self._template('login/pam.html', title='Login', + action='%s/login/pam' % self.basepath, + service_name=self.lm.service_name, + username_text=self.lm.username_text, + password_text=self.lm.password_text) + + def POST(self, *args, **kwargs): + username = None + password = None + user = None + for key, value in kwargs.iteritems(): + if key == 'login_name': + username = value + elif key == 'login_password': + password = value + if username is not None and password is not None: + user = self._authenticate(username, password) + else: + cherrypy.log.error("Error: Username or password is missing") + + if user: + return self.lm.auth_successful(user) + else: + return self.lm.auth_failed() + + def root(self, *args, **kwargs): + op = getattr(self, cherrypy.request.method, self.GET) + if callable(op): + return op(*args, **kwargs) + +class LoginManager(common.LoginManagerBase): + + def __init__(self, *args, **kwargs): + super(LoginManager, self).__init__(*args, **kwargs) + self.name = 'pam' + self.path = 'pam' + self.description = """ +Form based login Manager that uses the system's PAM infrastructure +for authentication. """ + self._options = { + 'service name': [ + """ The name of the PAM service used to authenticate. """, + 'string', + 'remote' + ], + 'username text': [ + """ The text shown to ask for the username in the form. """, + 'string', + 'Username' + ], + 'password text': [ + """ The text shown to ask for the password in the form. """, + 'string', + 'Password' + ], + } + + @property + def service_name(self): + return self.get_config_value('service name') + + @property + def username_text(self): + return self.get_config_value('username text') + + @property + def password_text(self): + return self.get_config_value('password text') + + def get_tree(self, site): + self.page = Pam(site, self) + return self.page diff --git a/ipsilon/login/common.py b/ipsilon/login/common.py new file mode 100755 index 0000000..7a11f18 --- /dev/null +++ b/ipsilon/login/common.py @@ -0,0 +1,158 @@ +#!/usr/bin/python +# +# Copyright (C) 2013 Simo Sorce <simo@redhat.com> +# +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +from util import data +from util import page +from util import user +from util import plugin +import cherrypy +import inspect +import os + +class LoginManagerBase(object): + + def __init__(self): + self.name = None + self.path = '/' + self._config = None + self._options = None + self.next_login = None + + def get_config_desc(self): + """ The configuration description is a dictionary that provides + A description of the supported configuration options, as well + as the default configuration option values. + The key is the option name, the value is an array of 3 elements: + - description + - option type + - default value + """ + return self._options + + def set_config(self, config): + self._config = config + + def get_config_value(self, name): + value = None + if self._config: + value = self._config.get(name, None) + if not value: + if self._options: + opt = self._options.get(name, None) + if opt: + value = opt[2] + + if cherrypy.config.get('debug', False): + cherrypy.log('[%s] %s: %s' % (self.name, name, value)) + + return value + + def set_config_value(self, option, value): + if not self._config: + self._config = dict() + self._config[option] = value + + def redirect_to_path(self, path): + base = cherrypy.config.get('base.mount', "") + raise cherrypy.HTTPRedirect('%s/login/%s' % (base, path)) + + def auth_successful(self, username): + # save ref before calling UserSession login() as it + # may regenerate the session + ref = '/idp' + if 'referral' in cherrypy.session: + ref = cherrypy.session['referral'] + + user.UserSession().login(username) + raise cherrypy.HTTPRedirect(ref) + + def auth_failed(self): + # Just make sure we destroy the session + user.UserSession().logout(None) + + if self.next_login: + return self.redirect_to_path(self.next_login.path) + + # FIXME: show an error page instead + raise cherrypy.HTTPError(401) + + +class LoginPageBase(page.Page): + + def __init__(self, site, mgr): + super(LoginPageBase, self).__init__(site) + self.lm = mgr + + def root(self, *args, **kwargs): + raise cherrypy.HTTPError(500) + + +class Login(page.Page): + + def __init__(self, *args, **kwargs): + super(Login, self).__init__(*args, **kwargs) + (whitelist, config) = data.Store().get_login_config() + if whitelist is not None: + self.plugin_whitelist = whitelist + else: + self.plugin_whitelist = [] + + self._site['login_plugins'] = dict() + login_plugins = self._site['login_plugins'] + login_plugins['config'] = config + + plugins = plugin.Plugins(path=cherrypy.config['base.dir']) + (login_path, login_file) = os.path.split(inspect.getfile(Login)) + login_plugins['available'] = plugins.get_plugins(login_path, 'LoginManager') + login_plugins['enabled'] = [] + + prev_obj = None + for item in login_plugins['available']: + self._log('Login plugin available: %s' % item) + if item not in self.plugin_whitelist: + continue + self._log('Login plugin enabled: %s' % item) + login_plugins['enabled'].append(item) + obj = login_plugins['available'][item] + if prev_obj: + prev_obj.next_login = obj + else: + self.first_login = obj + prev_obj = obj + if item in config: + obj.set_config(config[item]) + self.__dict__[item] = obj.get_tree(self._site) + + + def _log(self, fact): + if cherrypy.config.get('debug', False): + cherrypy.log(fact) + + def root(self, *args, **kwargs): + if self.first_login: + raise cherrypy.HTTPRedirect('%s/login/%s' % (self.basepath, + self.first_login.path)) + return self._template('login/index.html', title='Login') + + +class Logout(page.Page): + + def root(self, *args, **kwargs): + user.UserSession().logout(self.user) + return self._template('logout.html', title='Logout') |
