From 86b6c81f5eee55561797e8db47129a77e8ed1652 Mon Sep 17 00:00:00 2001 From: Patrick Uiterwijk Date: Oct 17 2016 09:01:31 +0000 Subject: Create an infofas plugin This will allow us to use FAS data even when the user was not authenticated with FAS. Signed-off-by: Patrick Uiterwijk Reviewed-by: Pierre-Yves Chibon --- diff --git a/ipsilon/info/infofas.py b/ipsilon/info/infofas.py new file mode 100644 index 0000000..9244606 --- /dev/null +++ b/ipsilon/info/infofas.py @@ -0,0 +1,177 @@ +# Copyright (C) 2014,2016 Ipsilon project Contributors, for license see COPYING + +from ipsilon.info.common import InfoProviderBase, InfoProviderInstaller +from ipsilon.util.plugin import PluginObject +from ipsilon.util.policy import Policy +from ipsilon.util import config as pconfig + +from fedora.client.fas2 import AccountSystem + + +try: + import openid_cla.cla as cla + + CLA_GROUPS = { + 'cla_click': cla.CLA_URI_FEDORA_CLICK, + 'cla_dell': cla.CLA_URI_FEDORA_DELL, + 'cla_done': cla.CLA_URI_FEDORA_DONE, + 'cla_fedora': cla.CLA_URI_FEDORA_FEDORA, + 'cla_fpca': cla.CLA_URI_FEDORA_FPCA, + 'cla_ibm': cla.CLA_URI_FEDORA_IBM, + 'cla_intel': cla.CLA_URI_FEDORA_INTEL, + 'cla_redhat': cla.CLA_URI_FEDORA_REDHAT, + } +except ImportError: + CLA_GROUPS = dict() + +fas_mapping = [ + ['username', 'nickname'], + ['telephone', 'phone'], + ['country_code', 'country'], + ['human_name', 'fullname'], + ['email', 'email'], + ['timezone', 'timezone'], + ['ssh_key', 'ssh_key'], + ['gpg_keyid', 'gpg_keyid'], +] + +fas_mapper = Policy(fas_mapping) + + +def fas_make_userdata(fas_data): + userdata, fas_extra = fas_mapper.map_attributes(fas_data) + + # We need to split ssh keys by newline, since we can't send newlines + userdata['ssh_key'] = userdata['ssh_key'].split('\n') + + # compute and store groups and cla groups + userdata['_groups'] = [] + userdata['_extras'] = {'fas': fas_extra, 'cla': []} + for group in fas_data.get('approved_memberships', {}): + if 'name' not in group: + continue + if group.get('group_type') == 'cla': + if group['name'] in CLA_GROUPS: + group_name = CLA_GROUPS[group['name']] + else: + group_name = group['name'] + userdata['_extras']['cla'].append(group_name) + else: + userdata['_groups'].append(group['name']) + + return userdata + + +class InfoProvider(InfoProviderBase): + + def __init__(self, *args): + super(InfoProvider, self).__init__(*args) + self._fas_client = None + self.name = 'fas' + self.description = """ +Info plugin that retrieves user data from FAS. """ + + self.new_config( + self.name, + pconfig.String( + 'FAS url', + 'The FAS Url.', + 'https://admin.fedoraproject.org/accounts/'), + pconfig.String( + 'FAS Proxy client user Agent', + 'The User Agent presented to the FAS Server.', + 'Ipsilon v1.0'), + pconfig.Condition( + 'FAS Insecure Auth', + 'If checked skips FAS server cert verification.', + False), + pconfig.String( + 'Bind Username', + 'Username to be used when retrieving info.', + 'ipsilondummy'), + pconfig.String( + 'Bind Password', + 'Username to be used when retrieving info.', + 'Password') + ) + + @property + def fas_url(self): + return self.get_config_value('FAS url') + + @property + def user_agent(self): + return self.get_config_value('FAS Proxy client user Agent') + + @property + def insecure(self): + return self.get_config_value('FAS Insecure Auth') + + @property + def bind_user(self): + return self.get_config_value('Bind Username') + + @property + def bind_pass(self): + return self.get_config_value('Bind Password') + + @property + def fas_client(self): + if not self._fas_client: + self._fas_client = AccountSystem(base_url=self.fas_url, + insecure=self.insecure, + useragent=self.user_agent, + username=self.bind_user, + password=self.bind_pass) + return self._fas_client + + def get_user_attrs(self, user): + if not self.fas_client: + return {} + try: + data = self.fas_client.person_by_username(user) + except Exception as ex: # pylint: disable=broad-except + self.error('Unable to retrieve info for %s: %s' % (user, ex)) + self.debug('URL: %s' % self.fas_url) + self.debug('username: %s' % self.bind_user) + return {} + if not data: + return {} + return fas_make_userdata(data) + + +class Installer(InfoProviderInstaller): + + def __init__(self, *pargs): + super(Installer, self).__init__() + self.name = 'fas' + self.pargs = pargs + + def install_args(self, group): + group.add_argument('--info-fas', choices=['yes', 'no'], default='no', + help='Configure FAS info') + group.add_argument('--info-fas-bind-username', action='store', + help='Username to use to retrieve FAS info') + group.add_argument('--info-fas-bind-password', action='store', + help='Password to use to retrieve FAS info') + + def configure(self, opts, changes): + if opts['info_fas'] != 'yes': + return + + # Add configuration data to database + po = PluginObject(*self.pargs) + po.name = 'fas' + po.wipe_data() + po.wipe_config_values() + + config = dict() + if 'info_fas_bind_username' in opts: + config['Bind Username'] = opts['info_fas_bind_username'] + if 'info_fas_bind_password' in opts: + config['Bind Password'] = opts['info_fas_bind_password'] + po.save_plugin_config(config) + + # Update global config to add login plugin + po.is_enabled = True + po.save_enabled_state() diff --git a/ipsilon/login/authfas.py b/ipsilon/login/authfas.py index 2177eba..28f6e80 100644 --- a/ipsilon/login/authfas.py +++ b/ipsilon/login/authfas.py @@ -2,8 +2,8 @@ from ipsilon.login.common import LoginFormBase, LoginManagerBase, \ LoginManagerInstaller +from ipsilon.info.infofas import fas_make_userdata from ipsilon.util.plugin import PluginObject -from ipsilon.util.policy import Policy from ipsilon.util import config as pconfig import cherrypy import logging @@ -12,39 +12,10 @@ from fedora.client.fasproxy import FasProxyClient from fedora.client import AuthError -try: - import openid_cla.cla as cla - - CLA_GROUPS = { - 'cla_click': cla.CLA_URI_FEDORA_CLICK, - 'cla_dell': cla.CLA_URI_FEDORA_DELL, - 'cla_done': cla.CLA_URI_FEDORA_DONE, - 'cla_fedora': cla.CLA_URI_FEDORA_FEDORA, - 'cla_fpca': cla.CLA_URI_FEDORA_FPCA, - 'cla_ibm': cla.CLA_URI_FEDORA_IBM, - 'cla_intel': cla.CLA_URI_FEDORA_INTEL, - 'cla_redhat': cla.CLA_URI_FEDORA_REDHAT, - } -except ImportError: - CLA_GROUPS = dict() - -fas_mapping = [ - ['username', 'nickname'], - ['telephone', 'phone'], - ['country_code', 'country'], - ['human_name', 'fullname'], - ['email', 'email'], - ['timezone', 'timezone'], - ['ssh_key', 'ssh_key'], - ['gpg_keyid', 'gpg_keyid'], -] - - class FAS(LoginFormBase): def __init__(self, site, mgr, page): super(FAS, self).__init__(site, mgr, page) - self.mapper = Policy(fas_mapping) def POST(self, *args, **kwargs): username = kwargs.get("login_name") @@ -63,7 +34,7 @@ class FAS(LoginFormBase): severity=logging.ERROR) if data and data.user: - userdata = self.make_userdata(data.user) + userdata = fas_make_userdata(data.user) return self.lm.auth_successful(self.trans, data.user['username'], userdata=userdata) @@ -83,29 +54,6 @@ class FAS(LoginFormBase): self.lm.set_auth_error() return self._template(self.formtemplate, **context) - def make_userdata(self, fas_data): - userdata, fas_extra = self.mapper.map_attributes(fas_data) - - # We need to split ssh keys by newline, since we can't send newlines - userdata['ssh_key'] = userdata['ssh_key'].split('\n') - - # compute and store groups and cla groups - userdata['_groups'] = [] - userdata['_extras'] = {'fas': fas_extra, 'cla': []} - for group in fas_data.get('approved_memberships', {}): - if 'name' not in group: - continue - if group.get('group_type') == 'cla': - if group['name'] in CLA_GROUPS: - group_name = CLA_GROUPS[group['name']] - else: - group_name = group['name'] - userdata['_extras']['cla'].append(group_name) - else: - userdata['_groups'].append(group['name']) - - return userdata - class LoginManager(LoginManagerBase):