996855cf08a5e85c0adf2cd74cc25ce668417b3a
[cascardo/ipsilon.git] / ipsilon / login / authfas.py
1 # Copyright (C) 2014 Ipsilon contributors, see COPYING file for license
2
3
4 from ipsilon.login.common import LoginFormBase, LoginManagerBase, \
5     LoginManagerInstaller
6 from ipsilon.util.plugin import PluginObject
7 from ipsilon.util.policy import Policy
8 from ipsilon.util import config as pconfig
9 import cherrypy
10 import logging
11
12 from fedora.client.fasproxy import FasProxyClient
13 from fedora.client import AuthError
14
15
16 try:
17     import openid_cla.cla as cla
18
19     CLA_GROUPS = {
20         'cla_click': cla.CLA_URI_FEDORA_CLICK,
21         'cla_dell': cla.CLA_URI_FEDORA_DELL,
22         'cla_done': cla.CLA_URI_FEDORA_DONE,
23         'cla_fedora': cla.CLA_URI_FEDORA_FEDORA,
24         'cla_fpca': cla.CLA_URI_FEDORA_FPCA,
25         'cla_ibm': cla.CLA_URI_FEDORA_IBM,
26         'cla_intel': cla.CLA_URI_FEDORA_INTEL,
27         'cla_redhat': cla.CLA_URI_FEDORA_REDHAT,
28     }
29 except ImportError:
30     CLA_GROUPS = dict()
31
32 fas_mapping = [
33     ['username', 'nickname'],
34     ['telephone', 'phone'],
35     ['country_code', 'country'],
36     ['human_name', 'fullname'],
37     ['email', 'email'],
38     ['timezone', 'timezone'],
39 ]
40
41
42 class FAS(LoginFormBase):
43
44     def __init__(self, site, mgr, page):
45         super(FAS, self).__init__(site, mgr, page)
46         self.mapper = Policy(fas_mapping)
47
48     def POST(self, *args, **kwargs):
49         username = kwargs.get("login_name")
50         password = kwargs.get("login_password")
51         error = None
52
53         if username and password:
54             data = None
55             try:
56                 _, data = self.lm.fpc.login(username, password)
57             except AuthError, e:
58                 cherrypy.log.error("Authentication error [%s]" % str(e),
59                                    severity=logging.ERROR)
60             except Exception, e:  # pylint: disable=broad-except
61                 cherrypy.log.error("Unknown Error [%s]" % str(e),
62                                    severity=logging.ERROR)
63
64             if data and data.user:
65                 userdata = self.make_userdata(data.user)
66                 return self.lm.auth_successful(self.trans,
67                                                data.user['username'],
68                                                userdata=userdata)
69             else:
70                 error = "Authentication failed"
71                 cherrypy.log.error(error, severity=logging.ERROR)
72         else:
73             error = "Username or password is missing"
74             cherrypy.log.error("Error: " + error, severity=logging.ERROR)
75
76         context = self.create_tmpl_context(
77             username=username,
78             error=error,
79             error_password=not password,
80             error_username=not username
81         )
82         self.lm.set_auth_error()
83         return self._template(self.formtemplate, **context)
84
85     def make_userdata(self, fas_data):
86         userdata, fas_extra = self.mapper.map_attributes(fas_data)
87
88         # compute and store groups and cla groups
89         userdata['_groups'] = []
90         userdata['_extras'] = {'fas': fas_extra, 'cla': []}
91         for group in fas_data.get('approved_memberships', {}):
92             if 'name' not in group:
93                 continue
94             if group.get('group_type') == 'cla':
95                 if group['name'] in CLA_GROUPS:
96                     group_name = CLA_GROUPS[group['name']]
97                 else:
98                     group_name = group['name']
99                 userdata['_extras']['cla'].append(group_name)
100             else:
101                 userdata['_groups'].append(group['name'])
102
103         return userdata
104
105
106 class LoginManager(LoginManagerBase):
107
108     def __init__(self, *args, **kwargs):
109         super(LoginManager, self).__init__(*args, **kwargs)
110         self.name = 'fas'
111         self.path = 'fas'
112         self.service_name = 'fas'
113         self.page = None
114         self.fpc = None
115         self.description = """
116 Form based login Manager that uses the Fedora Authentication Server
117 """
118         self.new_config(
119             self.name,
120             pconfig.String(
121                 'FAS url',
122                 'The FAS Url.',
123                 'https://admin.fedoraproject.org/accounts/'),
124             pconfig.String(
125                 'FAS Proxy client user Agent',
126                 'The User Agent presented to the FAS Server.',
127                 'Ipsilon v1.0'),
128             pconfig.Condition(
129                 'FAS Insecure Auth',
130                 'If checked skips FAS server cert verification.',
131                 False),
132             pconfig.String(
133                 'username text',
134                 'Text used to ask for the username at login time.',
135                 'FAS Username'),
136             pconfig.String(
137                 'password text',
138                 'Text used to ask for the password at login time.',
139                 'Password'),
140             pconfig.String(
141                 'help text',
142                 'Text used to guide the user at login time.',
143                 'Login with your FAS credentials')
144         )
145
146     @property
147     def help_text(self):
148         return self.get_config_value('help text')
149
150     @property
151     def username_text(self):
152         return self.get_config_value('username text')
153
154     @property
155     def password_text(self):
156         return self.get_config_value('password text')
157
158     @property
159     def fas_url(self):
160         return self.get_config_value('FAS url')
161
162     @property
163     def user_agent(self):
164         return self.get_config_value('FAS Proxy client user Agent')
165
166     @property
167     def insecure(self):
168         return self.get_config_value('FAS Insecure Auth')
169
170     def get_tree(self, site):
171         self.fpc = FasProxyClient(base_url=self.fas_url,
172                                   useragent=self.user_agent,
173                                   insecure=(self.insecure == 'YES'))
174         self.page = FAS(site, self, 'login/fas')
175         return self.page
176
177
178 class Installer(LoginManagerInstaller):
179
180     def __init__(self, *pargs):
181         super(Installer, self).__init__()
182         self.name = 'fas'
183         self.pargs = pargs
184
185     def install_args(self, group):
186         group.add_argument('--fas', choices=['yes', 'no'], default='no',
187                            help='Configure FAS authentication')
188
189     def configure(self, opts):
190         if opts['fas'] != 'yes':
191             return
192
193         # Add configuration data to database
194         po = PluginObject(*self.pargs)
195         po.name = 'fas'
196         po.wipe_data()
197         po.wipe_config_values()
198
199         # Update global config to add login plugin
200         po.is_enabled = True
201         po.save_enabled_state()