Add PAM configuration code
[cascardo/ipsilon.git] / ipsilon / login / authpam.py
1 #!/usr/bin/python
2 #
3 # Copyright (C) 2013  Simo Sorce <simo@redhat.com>
4 #
5 # see file 'COPYING' for use and warranty information
6 #
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.
11 #
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.
16 #
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/>.
19
20 from ipsilon.login.common import LoginPageBase, LoginManagerBase
21 from ipsilon.login.common import FACILITY
22 from ipsilon.util.plugin import PluginObject
23 import cherrypy
24 import pam
25
26
27 class Pam(LoginPageBase):
28
29     def _authenticate(self, username, password):
30         if self.lm.service_name:
31             ok = pam.authenticate(username, password, self.lm.service_name)
32         else:
33             ok = pam.authenticate(username, password)
34
35         if ok:
36             cherrypy.log("User %s successfully authenticated." % username)
37             return username
38
39         cherrypy.log("User %s failed authentication." % username)
40         return None
41
42     def GET(self, *args, **kwargs):
43         context = self.create_tmpl_context()
44         # pylint: disable=star-args
45         return self._template('login/pam.html', **context)
46
47     def POST(self, *args, **kwargs):
48         username = kwargs.get("login_name")
49         password = kwargs.get("login_password")
50         user = None
51         error = None
52
53         if username and password:
54             user = self._authenticate(username, password)
55             if user:
56                 return self.lm.auth_successful(user)
57             else:
58                 error = "Authentication failed"
59                 cherrypy.log.error(error)
60         else:
61             error = "Username or password is missing"
62             cherrypy.log.error("Error: " + error)
63
64         context = self.create_tmpl_context(
65             username=username,
66             error=error,
67             error_password=not password,
68             error_username=not username
69         )
70         # pylint: disable=star-args
71         return self._template('login/pam.html', **context)
72
73     def root(self, *args, **kwargs):
74         op = getattr(self, cherrypy.request.method, self.GET)
75         if callable(op):
76             return op(*args, **kwargs)
77
78     def create_tmpl_context(self, **kwargs):
79         next_url = None
80         if self.lm.next_login is not None:
81             next_url = self.lm.next_login.path
82
83         context = {
84             "title": 'Login',
85             "action": '%s/login/pam' % self.basepath,
86             "service_name": self.lm.service_name,
87             "username_text": self.lm.username_text,
88             "password_text": self.lm.password_text,
89             "description": self.lm.help_text,
90             "next_url": next_url,
91         }
92         context.update(kwargs)
93         return context
94
95
96 class LoginManager(LoginManagerBase):
97
98     def __init__(self, *args, **kwargs):
99         super(LoginManager, self).__init__(*args, **kwargs)
100         self.name = 'pam'
101         self.path = 'pam'
102         self.page = None
103         self.description = """
104 Form based login Manager that uses the system's PAM infrastructure
105 for authentication. """
106         self._options = {
107             'service name': [
108                 """ The name of the PAM service used to authenticate. """,
109                 'string',
110                 'remote'
111             ],
112             'help text': [
113                 """ The text shown to guide the user at login time. """,
114                 'string',
115                 'Insert your Username and Password and then submit.'
116             ],
117             'username text': [
118                 """ The text shown to ask for the username in the form. """,
119                 'string',
120                 'Username'
121             ],
122             'password text': [
123                 """ The text shown to ask for the password in the form. """,
124                 'string',
125                 'Password'
126             ],
127         }
128
129     @property
130     def service_name(self):
131         return self.get_config_value('service name')
132
133     @property
134     def help_text(self):
135         return self.get_config_value('help text')
136
137     @property
138     def username_text(self):
139         return self.get_config_value('username text')
140
141     @property
142     def password_text(self):
143         return self.get_config_value('password text')
144
145     def get_tree(self, site):
146         self.page = Pam(site, self)
147         return self.page
148
149
150 class Installer(object):
151
152     def __init__(self):
153         self.name = 'pam'
154         self.ptype = 'login'
155
156     def install_args(self, group):
157         group.add_argument('--pam', choices=['yes', 'no'], default='no',
158                            help='Configure PAM authentication')
159         group.add_argument('--pam-service', action='store', default='remote',
160                            help='PAM service name to use for authentication')
161
162     def configure(self, opts):
163         if opts['pam'] != 'yes':
164             return
165
166         # Add configuration data to database
167         po = PluginObject()
168         po.name = 'pam'
169         po.wipe_data()
170
171         po.wipe_config_values(FACILITY)
172         config = {'service name': opts['pam_service']}
173         po.set_config(config)
174         po.save_plugin_config(FACILITY)
175
176         # Update global config to add login plugin
177         po = PluginObject()
178         po.name = 'global'
179         globalconf = po.get_plugin_config(FACILITY)
180         if 'order' in globalconf:
181             order = globalconf['order'].split(',')
182         else:
183             order = []
184         order.append('pam')
185         globalconf['order'] = ','.join(order)
186         po.set_config(globalconf)
187         po.save_plugin_config(FACILITY)