summaryrefslogtreecommitdiffstats
path: root/ipsilon/login
diff options
context:
space:
mode:
authorPetr Vobornik <pvoborni@redhat.com>2014-01-23 15:00:51 +0100
committerPetr Vobornik <pvoborni@redhat.com>2014-01-24 19:07:05 +0100
commitefaed49f0c8069e3c3ddaf32be7bb8d26c8aae4c (patch)
tree91047b2a197bab1d613aab5028a6399ec67e3ff1 /ipsilon/login
parent96891701df4f1a3c2416663fcc84dde3de3e6bd7 (diff)
downloadipsilon-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__.py0
-rwxr-xr-xipsilon/login/authkrb.py78
-rwxr-xr-xipsilon/login/authpam.py112
-rwxr-xr-xipsilon/login/common.py158
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')