c2d8fff90974ec0a2b96ac2cf9fbcae0e9a4d396
[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 import cherrypy
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 = InfoMapping()
47         self.mapper.set_mapping(fas_mapping)
48
49     def POST(self, *args, **kwargs):
50         username = kwargs.get("login_name")
51         password = kwargs.get("login_password")
52         error = None
53
54         if username and password:
55             data = None
56             try:
57                 _, data = self.lm.fpc.login(username, password)
58             except AuthError, e:
59                 cherrypy.log.error("Authentication error [%s]" % str(e))
60             except Exception, e:  # pylint: disable=broad-except
61                 cherrypy.log.error("Unknown Error [%s]" % str(e))
62             if data and data.user:
63                 userdata = self.make_userdata(data.user)
64                 return self.lm.auth_successful(self.trans,
65                                                data.user['username'],
66                                                userdata=userdata)
67             else:
68                 error = "Authentication failed"
69                 cherrypy.log.error(error)
70         else:
71             error = "Username or password is missing"
72             cherrypy.log.error("Error: " + error)
73
74         context = self.create_tmpl_context(
75             username=username,
76             error=error,
77             error_password=not password,
78             error_username=not username
79         )
80         # pylint: disable=star-args
81         return self._template(self.formtemplate, **context)
82
83     def make_userdata(self, fas_data):
84         userdata, fas_extra = self.mapper.map_attrs(fas_data)
85
86         # compute and store groups and cla groups
87         userdata['groups'] = []
88         userdata['extras'] = {'fas': fas_extra, 'cla': []}
89         for group in fas_data.get('approved_memberships', {}):
90             if 'name' not in group:
91                 continue
92             if group.get('group_type') == 'cla':
93                 if group['name'] in CLA_GROUPS:
94                     userdata['extras']['cla'].append(CLA_GROUPS[group['name']])
95                 else:
96                     userdata['extras']['cla'].append(group['name'])
97             else:
98                 userdata['groups'].append(group['name'])
99
100         return userdata
101
102
103 class LoginManager(LoginManagerBase):
104
105     def __init__(self, *args, **kwargs):
106         super(LoginManager, self).__init__(*args, **kwargs)
107         self.name = 'fas'
108         self.path = 'fas'
109         self.service_name = 'fas'
110         self.page = None
111         self.fpc = None
112         self.description = """
113 Form based login Manager that uses the Fedora Authentication Server
114 """
115         self._options = {
116             'help text': [
117                 """ The text shown to guide the user at login time. """,
118                 'string',
119                 'Login wth your FAS credentials'
120             ],
121             'username text': [
122                 """ The text shown to ask for the username in the form. """,
123                 'string',
124                 'FAS Username'
125             ],
126             'password text': [
127                 """ The text shown to ask for the password in the form. """,
128                 'string',
129                 'Password'
130             ],
131             'FAS url': [
132                 """ The FAS Url. """,
133                 'string',
134                 'https://admin.fedoraproject.org/accounts/'
135             ],
136             'FAS Proxy client user Agent': [
137                 """ The User Agent presented to the FAS Server. """,
138                 'string',
139                 'Ipsilon v1.0'
140             ],
141             'FAS Insecure Auth': [
142                 """ If 'YES' skips FAS server cert verification. """,
143                 'string',
144                 ''
145             ],
146         }
147         self.conf_opt_order = ['FAS url', 'FAS Proxy client user Agent',
148                                'FAS Insecure Auth', 'username text',
149                                'password text', 'help text']
150
151     @property
152     def help_text(self):
153         return self.get_config_value('help text')
154
155     @property
156     def username_text(self):
157         return self.get_config_value('username text')
158
159     @property
160     def password_text(self):
161         return self.get_config_value('password text')
162
163     @property
164     def fas_url(self):
165         return self.get_config_value('FAS url')
166
167     @property
168     def user_agent(self):
169         return self.get_config_value('FAS Proxy client user Agent')
170
171     @property
172     def insecure(self):
173         return self.get_config_value('FAS Insecure Auth')
174
175     def get_tree(self, site):
176         self.fpc = FasProxyClient(base_url=self.fas_url,
177                                   useragent=self.user_agent,
178                                   insecure=(self.insecure == 'YES'))
179         self.page = FAS(site, self, 'login/fas')
180         return self.page
181
182
183 class Installer(object):
184
185     def __init__(self):
186         self.name = 'fas'
187         self.ptype = 'login'
188
189     def install_args(self, group):
190         group.add_argument('--fas', choices=['yes', 'no'], default='no',
191                            help='Configure FAS authentication')
192
193     def configure(self, opts):
194         if opts['fas'] != 'yes':
195             return
196
197         # Add configuration data to database
198         po = PluginObject()
199         po.name = 'fas'
200         po.wipe_data()
201
202         po.wipe_config_values(FACILITY)
203
204         # Update global config to add login plugin
205         po = PluginObject()
206         po.name = 'global'
207         globalconf = po.get_plugin_config(FACILITY)
208         if 'order' in globalconf:
209             order = globalconf['order'].split(',')
210         else:
211             order = []
212         order.append('fas')
213         globalconf['order'] = ','.join(order)
214         po.save_plugin_config(FACILITY, globalconf)