In configure we do not need to set_config()
[cascardo/ipsilon.git] / ipsilon / login / common.py
index 5470626..028b754 100755 (executable)
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 # 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 ipsilon.util.log import Log
 from ipsilon.util.page import Page
 from ipsilon.util.user import UserSession
 from ipsilon.util.plugin import PluginLoader, PluginObject
 from ipsilon.util.plugin import PluginInstaller
 from ipsilon.util.page import Page
 from ipsilon.util.user import UserSession
 from ipsilon.util.plugin import PluginLoader, PluginObject
 from ipsilon.util.plugin import PluginInstaller
+from ipsilon.info.common import Info
+from ipsilon.util.cookies import SecureCookie
 import cherrypy
 
 
 import cherrypy
 
 
-class LoginManagerBase(PluginObject, Log):
+USERNAME_COOKIE = 'ipsilon_default_username'
+
+
+class LoginManagerBase(PluginObject):
 
     def __init__(self):
         super(LoginManagerBase, self).__init__()
 
     def __init__(self):
         super(LoginManagerBase, self).__init__()
+        self._site = None
         self.path = '/'
         self.next_login = None
         self.path = '/'
         self.next_login = None
+        self.info = None
+        self.is_enabled = False
 
     def redirect_to_path(self, path):
         base = cherrypy.config.get('base.mount', "")
         raise cherrypy.HTTPRedirect('%s/login/%s' % (base, path))
 
 
     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, userdata=None):
-        # save ref before calling UserSession login() as it
-        # may regenerate the session
+    def auth_successful(self, trans, username, auth_type=None, userdata=None):
         session = UserSession()
         session = UserSession()
-        ref = session.get_data('login', 'Return')
-        if not ref:
-            ref = cherrypy.config.get('base.mount', "") + '/'
 
 
-        session.login(username, userdata)
+        if self.info:
+            userattrs = self.info.get_user_attrs(username)
+            if userdata:
+                userdata.update(userattrs.get('userdata', {}))
+            else:
+                userdata = userattrs.get('userdata', {})
+
+            # merge groups and extras from login plugin and info plugin
+            userdata['groups'] = list(set(userdata.get('groups', []) +
+                                          userattrs.get('groups', [])))
 
 
-        raise cherrypy.HTTPRedirect(ref)
+            userdata['extras'] = userdata.get('extras', {})
+            userdata['extras'].update(userattrs.get('extras', {}))
+
+            self.debug("User %s attributes: %s" % (username, repr(userdata)))
+
+        if auth_type:
+            if userdata:
+                userdata.update({'auth_type': auth_type})
+            else:
+                userdata = {'auth_type': auth_type}
+
+        # create session login including all the userdata just gathered
+        session.login(username, userdata)
 
 
-    def auth_failed(self):
+        # save username into a cookie if parent was form base auth
+        if auth_type == 'password':
+            cookie = SecureCookie(USERNAME_COOKIE, username)
+            # 15 days
+            cookie.maxage = 1296000
+            cookie.send()
+
+        transdata = trans.retrieve()
+        self.debug(transdata)
+        redirect = transdata.get('login_return',
+                                 cherrypy.config.get('base.mount', "") + '/')
+        self.debug('Redirecting back to: %s' % redirect)
+
+        # on direct login the UI (ie not redirected by a provider) we ned to
+        # remove the transaction cookie as it won't be needed anymore
+        if trans.provider == 'login':
+            self.debug('Wiping transaction data')
+            trans.wipe()
+        raise cherrypy.HTTPRedirect(redirect)
+
+    def auth_failed(self, trans):
         # try with next module
         if self.next_login:
             return self.redirect_to_path(self.next_login.path)
 
         # return to the caller if any
         session = UserSession()
         # try with next module
         if self.next_login:
             return self.redirect_to_path(self.next_login.path)
 
         # return to the caller if any
         session = UserSession()
-        ref = session.get_data('login', 'Return')
 
 
-        # otherwise destroy session and return error
-        if not ref:
+        transdata = trans.retrieve()
+
+        # on direct login the UI (ie not redirected by a provider) we ned to
+        # remove the transaction cookie as it won't be needed anymore
+        if trans.provider == 'login':
+            trans.wipe()
+
+        # destroy session and return error
+        if 'login_return' not in transdata:
             session.logout(None)
             raise cherrypy.HTTPError(401)
 
             session.logout(None)
             raise cherrypy.HTTPError(401)
 
-        raise cherrypy.HTTPRedirect(ref)
+        raise cherrypy.HTTPRedirect(transdata['login_return'])
 
     def get_tree(self, site):
         raise NotImplementedError
 
     def enable(self, site):
 
     def get_tree(self, site):
         raise NotImplementedError
 
     def enable(self, site):
-        plugins = site[FACILITY]
-        if self in plugins['enabled']:
+        if self.is_enabled:
             return
 
             return
 
+        if not self._site:
+            self._site = site
+        plugins = self._site[FACILITY]
+
         # configure self
         if self.name in plugins['config']:
             self.set_config(plugins['config'][self.name])
         # configure self
         if self.name in plugins['config']:
             self.set_config(plugins['config'][self.name])
@@ -93,13 +145,18 @@ class LoginManagerBase(PluginObject, Log):
             root.first_login = self
 
         plugins['enabled'].append(self)
             root.first_login = self
 
         plugins['enabled'].append(self)
+        self.is_enabled = True
         self._debug('Login plugin enabled: %s' % self.name)
 
         self._debug('Login plugin enabled: %s' % self.name)
 
+        # Get handle of the info plugin
+        self.info = root.info
+
     def disable(self, site):
     def disable(self, site):
-        plugins = site[FACILITY]
-        if self not in plugins['enabled']:
+        if not self.is_enabled:
             return
 
             return
 
+        plugins = self._site[FACILITY]
+
         # remove self from chain
         root = plugins['root']
         if root.first_login == self:
         # remove self from chain
         root = plugins['root']
         if root.first_login == self:
@@ -113,6 +170,7 @@ class LoginManagerBase(PluginObject, Log):
         self.next_login = None
 
         plugins['enabled'].remove(self)
         self.next_login = None
 
         plugins['enabled'].remove(self)
+        self.is_enabled = False
         self._debug('Login plugin disabled: %s' % self.name)
 
 
         self._debug('Login plugin disabled: %s' % self.name)
 
 
@@ -121,11 +179,71 @@ class LoginPageBase(Page):
     def __init__(self, site, mgr):
         super(LoginPageBase, self).__init__(site)
         self.lm = mgr
     def __init__(self, site, mgr):
         super(LoginPageBase, self).__init__(site)
         self.lm = mgr
+        self._Transaction = None
 
     def root(self, *args, **kwargs):
         raise cherrypy.HTTPError(500)
 
 
 
     def root(self, *args, **kwargs):
         raise cherrypy.HTTPError(500)
 
 
+class LoginFormBase(LoginPageBase):
+
+    def __init__(self, site, mgr, page, template=None):
+        super(LoginFormBase, self).__init__(site, mgr)
+        self.formpage = page
+        self.formtemplate = template or 'login/form.html'
+        self.trans = None
+
+    def GET(self, *args, **kwargs):
+        context = self.create_tmpl_context()
+        # pylint: disable=star-args
+        return self._template(self.formtemplate, **context)
+
+    def root(self, *args, **kwargs):
+        self.trans = self.get_valid_transaction('login', **kwargs)
+        op = getattr(self, cherrypy.request.method, self.GET)
+        if callable(op):
+            return op(*args, **kwargs)
+
+    def create_tmpl_context(self, **kwargs):
+        next_url = None
+        if self.lm.next_login is not None:
+            next_url = '%s?%s' % (self.lm.next_login.path,
+                                  self.trans.get_GET_arg())
+
+        cookie = SecureCookie(USERNAME_COOKIE)
+        cookie.receive()
+        username = cookie.value
+        if username is None:
+            username = ''
+
+        target = None
+        if self.trans is not None:
+            tid = self.trans.transaction_id
+            target = self.trans.retrieve().get('login_target')
+        if tid is None:
+            tid = ''
+
+        context = {
+            "title": 'Login',
+            "action": '%s/%s' % (self.basepath, self.formpage),
+            "service_name": self.lm.service_name,
+            "username_text": self.lm.username_text,
+            "password_text": self.lm.password_text,
+            "description": self.lm.help_text,
+            "next_url": next_url,
+            "username": username,
+            "login_target": target,
+            "cancel_url": '%s/login/cancel?%s' % (self.basepath,
+                                                  self.trans.get_GET_arg()),
+        }
+        context.update(kwargs)
+        if self.trans is not None:
+            t = self.trans.get_POST_tuple()
+            context.update({t[0]: t[1]})
+
+        return context
+
+
 FACILITY = 'login_config'
 
 
 FACILITY = 'login_config'
 
 
@@ -133,7 +251,9 @@ class Login(Page):
 
     def __init__(self, *args, **kwargs):
         super(Login, self).__init__(*args, **kwargs)
 
     def __init__(self, *args, **kwargs):
         super(Login, self).__init__(*args, **kwargs)
+        self.cancel = Cancel(*args, **kwargs)
         self.first_login = None
         self.first_login = None
+        self.info = Info(self._site)
 
         loader = PluginLoader(Login, FACILITY, 'LoginManager')
         self._site[FACILITY] = loader.get_plugin_data()
 
         loader = PluginLoader(Login, FACILITY, 'LoginManager')
         self._site[FACILITY] = loader.get_plugin_data()
@@ -154,9 +274,11 @@ class Login(Page):
 
     def root(self, *args, **kwargs):
         if self.first_login:
 
     def root(self, *args, **kwargs):
         if self.first_login:
-            raise cherrypy.HTTPRedirect('%s/login/%s' %
-                                        (self.basepath,
-                                         self.first_login.path))
+            trans = self.get_valid_transaction('login', **kwargs)
+            redirect = '%s/login/%s?%s' % (self.basepath,
+                                           self.first_login.path,
+                                           trans.get_GET_arg())
+            raise cherrypy.HTTPRedirect(redirect)
         return self._template('login/index.html', title='Login')
 
 
         return self._template('login/index.html', title='Login')
 
 
@@ -167,6 +289,25 @@ class Logout(Page):
         return self._template('logout.html', title='Logout')
 
 
         return self._template('logout.html', title='Logout')
 
 
+class Cancel(Page):
+
+    def GET(self, *args, **kwargs):
+
+        session = UserSession()
+        session.logout(None)
+
+        # return to the caller if any
+        transdata = self.get_valid_transaction('login', **kwargs).retrieve()
+        if 'login_return' not in transdata:
+            raise cherrypy.HTTPError(401)
+        raise cherrypy.HTTPRedirect(transdata['login_return'])
+
+    def root(self, *args, **kwargs):
+        op = getattr(self, cherrypy.request.method, self.GET)
+        if callable(op):
+            return op(*args, **kwargs)
+
+
 class LoginMgrsInstall(object):
 
     def __init__(self):
 class LoginMgrsInstall(object):
 
     def __init__(self):