Add uninstallation support.
[cascardo/ipsilon.git] / ipsilon / login / authfas.py
1 # Copyright (C) 2014 Ipsilon contributors, see COPYING file for license
2
3
4 from ipsilon.login.common import LoginFormBase, LoginManagerBase, \
5     LoginManagerInstaller
6 from ipsilon.util.plugin import PluginObject
7 from ipsilon.util.policy import Policy
8 from ipsilon.util import config as pconfig
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 fas_mapping = [
32     ['username', 'nickname'],
33     ['telephone', 'phone'],
34     ['country_code', 'country'],
35     ['human_name', 'fullname'],
36     ['email', 'email'],
37     ['timezone', 'timezone'],
38 ]
39
40
41 class FAS(LoginFormBase):
42
43     def __init__(self, site, mgr, page):
44         super(FAS, self).__init__(site, mgr, page)
45         self.mapper = Policy(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_attributes(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                     group_name = CLA_GROUPS[group['name']]
93                 else:
94                     group_name = group['name']
95                 userdata['_extras']['cla'].append(group_name)
96             else:
97                 userdata['_groups'].append(group['name'])
98
99         return userdata
100
101
102 class LoginManager(LoginManagerBase):
103
104     def __init__(self, *args, **kwargs):
105         super(LoginManager, self).__init__(*args, **kwargs)
106         self.name = 'fas'
107         self.path = 'fas'
108         self.service_name = 'fas'
109         self.page = None
110         self.fpc = None
111         self.description = """
112 Form based login Manager that uses the Fedora Authentication Server
113 """
114         self.new_config(
115             self.name,
116             pconfig.String(
117                 'FAS url',
118                 'The FAS Url.',
119                 'https://admin.fedoraproject.org/accounts/'),
120             pconfig.String(
121                 'FAS Proxy client user Agent',
122                 'The User Agent presented to the FAS Server.',
123                 'Ipsilon v1.0'),
124             pconfig.Condition(
125                 'FAS Insecure Auth',
126                 'If checked skips FAS server cert verification.',
127                 False),
128             pconfig.String(
129                 'username text',
130                 'Text used to ask for the username at login time.',
131                 'FAS Username'),
132             pconfig.String(
133                 'password text',
134                 'Text used to ask for the password at login time.',
135                 'Password'),
136             pconfig.String(
137                 'help text',
138                 'Text used to guide the user at login time.',
139                 'Login with your FAS credentials')
140         )
141
142     @property
143     def help_text(self):
144         return self.get_config_value('help text')
145
146     @property
147     def username_text(self):
148         return self.get_config_value('username text')
149
150     @property
151     def password_text(self):
152         return self.get_config_value('password text')
153
154     @property
155     def fas_url(self):
156         return self.get_config_value('FAS url')
157
158     @property
159     def user_agent(self):
160         return self.get_config_value('FAS Proxy client user Agent')
161
162     @property
163     def insecure(self):
164         return self.get_config_value('FAS Insecure Auth')
165
166     def get_tree(self, site):
167         self.fpc = FasProxyClient(base_url=self.fas_url,
168                                   useragent=self.user_agent,
169                                   insecure=(self.insecure == 'YES'))
170         self.page = FAS(site, self, 'login/fas')
171         return self.page
172
173
174 class Installer(LoginManagerInstaller):
175
176     def __init__(self, *pargs):
177         super(Installer, self).__init__()
178         self.name = 'fas'
179         self.pargs = pargs
180
181     def install_args(self, group):
182         group.add_argument('--fas', choices=['yes', 'no'], default='no',
183                            help='Configure FAS authentication')
184
185     def configure(self, opts):
186         if opts['fas'] != 'yes':
187             return
188
189         # Add configuration data to database
190         po = PluginObject(*self.pargs)
191         po.name = 'fas'
192         po.wipe_data()
193         po.wipe_config_values()
194
195         # Update global config to add login plugin
196         po.is_enabled = True
197         po.save_enabled_state()