Add Info providers Admin pages
[cascardo/ipsilon.git] / ipsilon / login / common.py
index d290521..e59085f 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
 import cherrypy
 
 
 import cherrypy
 
 
-class LoginManagerBase(PluginObject):
+USERNAME_COOKIE = 'ipsilon_default_username'
+
+
+class LoginManagerBase(PluginObject, Log):
 
     def __init__(self):
         super(LoginManagerBase, self).__init__()
         self.path = '/'
         self.next_login = None
 
     def __init__(self):
         super(LoginManagerBase, self).__init__()
         self.path = '/'
         self.next_login = None
+        self.info = None
 
     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):
+    def auth_successful(self, username, auth_type=None, userdata=None):
         # save ref before calling UserSession login() as it
         # may regenerate the session
         session = UserSession()
         # save ref before calling UserSession login() as it
         # may regenerate the session
         session = UserSession()
@@ -43,12 +49,31 @@ class LoginManagerBase(PluginObject):
         if not ref:
             ref = cherrypy.config.get('base.mount', "") + '/'
 
         if not ref:
             ref = cherrypy.config.get('base.mount', "") + '/'
 
-        session.login(username)
+        if self.info:
+            userattrs = self.info.get_user_attrs(username)
+            if userdata:
+                userdata.update(userattrs or {})
+            else:
+                userdata = userattrs
+            self.debug("User %s attributes: %s" % (username, repr(userdata)))
 
 
-        # Save additional data provided by the login manager
-        if userdata:
-            for key in userdata:
-                session.save_data('user', key, userdata[key])
+        if auth_type:
+            if userdata:
+                userdata.update({'auth_type': auth_type})
+            else:
+                userdata = {'auth_type': auth_type}
+
+        session.login(username, userdata)
+
+        # save username into a cookie if parent was form base auth
+        if auth_type == 'password':
+            cherrypy.response.cookie[USERNAME_COOKIE] = username
+            cherrypy.response.cookie[USERNAME_COOKIE]['path'] = \
+                cherrypy.config.get('base.mount', '/')
+            cherrypy.response.cookie[USERNAME_COOKIE]['secure'] = True
+            cherrypy.response.cookie[USERNAME_COOKIE]['httponly'] = True
+            # 15 days
+            cherrypy.response.cookie[USERNAME_COOKIE]['max-age'] = 1296000
 
         raise cherrypy.HTTPRedirect(ref)
 
 
         raise cherrypy.HTTPRedirect(ref)
 
@@ -68,6 +93,60 @@ class LoginManagerBase(PluginObject):
 
         raise cherrypy.HTTPRedirect(ref)
 
 
         raise cherrypy.HTTPRedirect(ref)
 
+    def get_tree(self, site):
+        raise NotImplementedError
+
+    def enable(self, site):
+        plugins = site[FACILITY]
+        if self in plugins['enabled']:
+            return
+
+        # configure self
+        if self.name in plugins['config']:
+            self.set_config(plugins['config'][self.name])
+
+        # and add self to the root
+        root = plugins['root']
+        root.add_subtree(self.name, self.get_tree(site))
+
+        # finally add self in login chain
+        prev_obj = None
+        for prev_obj in plugins['enabled']:
+            if prev_obj.next_login:
+                break
+        if prev_obj:
+            while prev_obj.next_login:
+                prev_obj = prev_obj.next_login
+            prev_obj.next_login = self
+        if not root.first_login:
+            root.first_login = self
+
+        plugins['enabled'].append(self)
+        self._debug('Login plugin enabled: %s' % self.name)
+
+        # Get handle of the info plugin
+        self.info = root.info
+
+    def disable(self, site):
+        plugins = site[FACILITY]
+        if self not in plugins['enabled']:
+            return
+
+        # remove self from chain
+        root = plugins['root']
+        if root.first_login == self:
+            root.first_login = self.next_login
+        elif root.first_login:
+            prev_obj = root.first_login
+            while prev_obj.next_login != self:
+                prev_obj = prev_obj.next_login
+            if prev_obj:
+                prev_obj.next_login = self.next_login
+        self.next_login = None
+
+        plugins['enabled'].remove(self)
+        self._debug('Login plugin disabled: %s' % self.name)
+
 
 class LoginPageBase(Page):
 
 
 class LoginPageBase(Page):
 
@@ -79,6 +158,46 @@ class LoginPageBase(Page):
         raise cherrypy.HTTPError(500)
 
 
         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'
+
+    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):
+        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 = self.lm.next_login.path
+
+        username = ''
+        if USERNAME_COOKIE in cherrypy.request.cookie:
+            username = cherrypy.request.cookie[USERNAME_COOKIE].value
+
+        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,
+        }
+        context.update(kwargs)
+        return context
+
+
 FACILITY = 'login_config'
 
 
 FACILITY = 'login_config'
 
 
@@ -87,6 +206,7 @@ class Login(Page):
     def __init__(self, *args, **kwargs):
         super(Login, self).__init__(*args, **kwargs)
         self.first_login = None
     def __init__(self, *args, **kwargs):
         super(Login, self).__init__(*args, **kwargs)
         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()
@@ -95,22 +215,15 @@ class Login(Page):
         available = plugins['available'].keys()
         self._debug('Available login managers: %s' % str(available))
 
         available = plugins['available'].keys()
         self._debug('Available login managers: %s' % str(available))
 
-        prev_obj = None
+        plugins['root'] = self
         for item in plugins['whitelist']:
             self._debug('Login plugin in whitelist: %s' % item)
             if item not in plugins['available']:
                 continue
         for item in plugins['whitelist']:
             self._debug('Login plugin in whitelist: %s' % item)
             if item not in plugins['available']:
                 continue
-            self._debug('Login plugin enabled: %s' % item)
-            plugins['enabled'].append(item)
-            obj = plugins['available'][item]
-            if prev_obj:
-                prev_obj.next_login = obj
-            else:
-                self.first_login = obj
-            prev_obj = obj
-            if item in plugins['config']:
-                obj.set_config(plugins['config'][item])
-            self.__dict__[item] = obj.get_tree(self._site)
+            plugins['available'][item].enable(self._site)
+
+    def add_subtree(self, name, page):
+        self.__dict__[name] = page
 
     def root(self, *args, **kwargs):
         if self.first_login:
 
     def root(self, *args, **kwargs):
         if self.first_login: