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