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