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