cb1c324b87eff37094f9fab0e869b0e57f47521c
[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.util.plugin import PluginObject
9 from ipsilon.util import config as pconfig
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.new_config(
116             self.name,
117             pconfig.String(
118                 'FAS url',
119                 'The FAS Url.',
120                 'https://admin.fedoraproject.org/accounts/'),
121             pconfig.String(
122                 'FAS Proxy client user Agent',
123                 'The User Agent presented to the FAS Server.',
124                 'Ipsilon v1.0'),
125             pconfig.Condition(
126                 'FAS Insecure Auth',
127                 'If checked skips FAS server cert verification.',
128                 False),
129             pconfig.String(
130                 'username text',
131                 'Text used to ask for the username at login time.',
132                 'FAS Username'),
133             pconfig.String(
134                 'password text',
135                 'Text used to ask for the password at login time.',
136                 'Password'),
137             pconfig.String(
138                 'help text',
139                 'Text used to guide the user at login time.',
140                 'Login with your FAS credentials')
141         )
142
143     @property
144     def help_text(self):
145         return self.get_config_value('help text')
146
147     @property
148     def username_text(self):
149         return self.get_config_value('username text')
150
151     @property
152     def password_text(self):
153         return self.get_config_value('password text')
154
155     @property
156     def fas_url(self):
157         return self.get_config_value('FAS url')
158
159     @property
160     def user_agent(self):
161         return self.get_config_value('FAS Proxy client user Agent')
162
163     @property
164     def insecure(self):
165         return self.get_config_value('FAS Insecure Auth')
166
167     def get_tree(self, site):
168         self.fpc = FasProxyClient(base_url=self.fas_url,
169                                   useragent=self.user_agent,
170                                   insecure=(self.insecure == 'YES'))
171         self.page = FAS(site, self, 'login/fas')
172         return self.page
173
174
175 class Installer(object):
176
177     def __init__(self, *pargs):
178         self.name = 'fas'
179         self.ptype = 'login'
180         self.pargs = pargs
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(*self.pargs)
192         po.name = 'fas'
193         po.wipe_data()
194         po.wipe_config_values()
195
196         # Update global config to add login plugin
197         po.is_enabled = True
198         po.save_enabled_state()