commit e47f0a96c4c376120369238f6a670638b0296c0d
Author: Xavier Lamien <laxathom(a)scm1.rpmfusion.org>
Date: Thu Sep 5 22:26:31 2013 +0200
Add fas-client backport on release 0.8.3.2 before we move to lastest HEAD.
modules/fas/files/hotfix/fas2.py | 769 ++++++++++++++++++++++++++++++++++++
modules/fas/files/hotfix/fasClient | 629 +++++++++++++++++++++++++++++
modules/fas/manifests/init.pp | 17 +-
3 files changed, 1414 insertions(+), 1 deletions(-)
---
diff --git a/modules/fas/files/hotfix/fas2.py b/modules/fas/files/hotfix/fas2.py
new file mode 100644
index 0000000..5038efb
--- /dev/null
+++ b/modules/fas/files/hotfix/fas2.py
@@ -0,0 +1,769 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2008-2012 Ricky Zhou, Red Hat, Inc.
+# This file is part of python-fedora
+#
+# python-fedora is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# python-fedora is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with python-fedora; if not, see <
http://www.gnu.org/licenses/>
+#
+'''
+Provide a client module for talking to the Fedora Account System.
+
+
+.. moduleauthor:: Ricky Zhou <ricky(a)fedoraproject.org>
+.. moduleauthor:: Toshio Kuratomi <tkuratom(a)redhat.com>
+.. moduleauthor:: Ralph Bean <rbean(a)redhat.com>
+'''
+import itertools
+import urllib
+import warnings
+
+from bunch import Bunch
+from kitchen.text.converters import to_bytes
+
+try:
+ from hashlib import md5
+except ImportError:
+ from md5 import new as md5
+
+from fedora.client import AppError, BaseClient, FasProxyClient, \
+ FedoraClientError, FedoraServiceError
+from fedora import __version__, b_
+
+### FIXME: To merge:
+# /usr/bin/fasClient from fas
+# API from Will Woods
+# API from MyFedora
+
+class FASError(FedoraClientError):
+ '''FAS Error'''
+ pass
+
+class CLAError(FASError):
+ '''CLA Error'''
+ pass
+
+USERFIELDS = ['affiliation', 'bugzilla_email',
'certificate_serial',
+ 'comments', 'country_code', 'creation', 'email',
'emailtoken',
+ 'facsimile', 'gpg_keyid', 'human_name', 'id',
'internal_comments',
+ 'ircnick', 'latitude', 'last_seen', 'longitude',
'password',
+ 'password_changed', 'passwordtoken', 'postal_address',
'privacy',
+ 'locale', 'ssh_key', 'status', 'status_change',
'telephone',
+ 'unverified_email', 'timezone', 'username', ]
+
+class AccountSystem(BaseClient):
+ '''An object for querying the Fedora Account System.
+
+ The Account System object provides a python API for talking to the Fedora
+ Account System. It abstracts the http requests, cookie handling, and
+ other details so you can concentrate on the methods that are important to
+ your program.
+
+ .. versionchanged:: 0.3.26
+ Added :meth:`~fedora.client.AccountSystem.gravatar_url` that returns
+ a url to a gravatar for a user.
+ '''
+ # proxy is a thread-safe connection to the fas server for verifying
+ # passwords of other users
+ proxy = None
+
+ # size that we allow to request from
gravatar.com
+ _valid_gravatar_sizes = (32, 64, 140)
+
+ def __init__(self,
base_url='https://admin.fedoraproject.org/accounts/';,
+ *args, **kwargs):
+ '''Create the AccountSystem client object.
+
+ :kwargs base_url: Base of every URL used to contact the server.
+ Defaults to the Fedora Project FAS instance.
+ :kwargs useragent: useragent string to use. If not given, default to
+ "Fedora Account System Client/VERSION"
+ :kwargs debug: If True, log debug information
+ :kwargs username: username for establishing authenticated connections
+ :kwargs password: password to use with authenticated connections
+ :kwargs session_cookie: **Deprecated** Use session_id instead.
+ User's session_cookie to connect to the server
+ :kwargs session_id: user's session_id to connect to the server
+ :kwargs cache_session: if set to true, cache the user's session cookie
+ on the filesystem between runs.
+ '''
+ if 'useragent' not in kwargs:
+ kwargs['useragent'] = 'Fedora Account System Client/%s' \
+ % __version__
+ super(AccountSystem, self).__init__(base_url, *args, **kwargs)
+ # We need a single proxy for the class to verify username/passwords
+ # against.
+ if not self.proxy:
+ self.proxy = FasProxyClient(base_url, useragent=self.useragent,
+ session_as_cookie=False, debug=self.debug,
+ insecure=self.insecure)
+
+ # Preseed a list of FAS accounts with bugzilla addresses
+ # This allows us to specify a different email for bugzilla than is
+ # in the FAS db. It is a hack, however, until FAS has a field for the
+ # bugzilla address.
+ self.__bugzilla_email = {
+ # Konstantin Ryabitsev: mricon(a)gmail.com
+ 100029: 'icon(a)fedoraproject.org',
+ # Sean Reifschneider: jafo(a)tummy.com
+ 100488: 'jafo-redhat(a)tummy.com',
+ # Karen Pease: karen-pease(a)uiowa.edu
+ 100281: 'meme(a)daughtersoftiresias.org',
+ # Robert Scheck: redhat(a)linuxnetz.de
+ 100093: 'redhat-bugzilla(a)linuxnetz.de',
+ # Scott Bakers: bakers(a)web-ster.com
+ 100881: 'scott(a)perturb.org',
+ # Colin Charles: byte(a)aeon.com.my
+ 100014: 'byte(a)fedoraproject.org',
+ # W. Michael Petullo: mike(a)flyn.org
+ 100136: 'redhat(a)flyn.org',
+ # Elliot Lee: sopwith+fedora(a)gmail.com
+ 100060: 'sopwith(a)redhat.com',
+ # Control Center Team: Bugzilla user but email doesn't exist
+ 9908: 'control-center-maint(a)redhat.com',
+ # Máirín Duffy
+ 100548: 'duffy(a)redhat.com',
+ # Muray McAllister: murray.mcallister(a)gmail.com
+ 102321: 'mmcallis(a)redhat.com',
+ # William Jon McCann: mccann(a)jhu.edu
+ 102489: 'jmccann(a)redhat.com',
+ # Matt Domsch's rebuild script -- bz email goes to /dev/null
+ 103590: 'ftbfs(a)fedoraproject.org',
+ # Sindre Pedersen Bjørdal: foolish(a)guezz.net
+ 100460 : 'sindrepb(a)fedoraproject.org',
+ # Jesus M. Rodriguez: jmrodri(a)gmail.com
+ 102180: 'jesusr(a)redhat.com',
+ # Roozbeh Pournader: roozbeh(a)farsiweb.info
+ 100350: 'roozbeh(a)gmail.com',
+ # Michael DeHaan: michael.dehaan(a)gmail.com
+ 100603: 'mdehaan(a)redhat.com',
+ # Sebastian Gosenheimer: sgosenheimer(a)googlemail.com
+ 103647: 'sebastian.gosenheimer(a)proio.com',
+ # Ben Konrath: bkonrath(a)redhat.com
+ 101156: 'ben(a)bagu.org',
+ # Kai Engert: kaie(a)redhat.com
+ 100399: 'kengert(a)redhat.com',
+ # William Jon McCann: william.jon.mccann(a)gmail.com
+ 102952: 'jmccann(a)redhat.com',
+ # Simon Wesp: simon(a)w3sp.de
+ 109464: 'cassmodiah(a)fedoraproject.org',
+ # Robert M. Albrecht: romal(a)gmx.de
+ 101475: 'mail(a)romal.de',
+ # Mathieu Bridon: mathieu.bridon(a)gmail.com
+ 100753: 'bochecha(a)fedoraproject.org',
+ # Davide Cescato: davide.cescato(a)iaeste.ch
+ 123204: 'ceski(a)fedoraproject.org',
+ # Nick Bebout: nick(a)bebout.net
+ 101458: 'nb(a)fedoraproject.org',
+ # Niels Haase: haase.niels(a)gmail.com
+ 126862: 'arxs(a)fedoraproject.org',
+ # Thomas Janssen: th.p.janssen(a)googlemail.com
+ 103110: 'thomasj(a)fedoraproject.org',
+ # Michael J Gruber: 'michaeljgruber+fedoraproject(a)gmail.com'
+ 105113: 'mjg(a)fedoraproject.org',
+ # Juan Manuel Rodriguez Moreno: 'nushio(a)gmail.com'
+ 101302: 'nushio(a)fedoraproject.org',
+ # Andrew Cagney: 'andrew.cagney(a)gmail.com'
+ 102169: 'cagney(a)fedoraproject.org',
+ # Jeremy Katz: 'jeremy(a)katzbox.net'
+ 100036: 'katzj(a)fedoraproject.org',
+ # Dominic Hopf: 'dmaphy(a)gmail.com'
+ 124904: 'dmaphy(a)fedoraproject.org',
+ # Christoph Wickert: 'christoph.wickert(a)googlemail.com':
+ 100271: 'cwickert(a)fedoraproject.org',
+ # Elliott Baron: 'elliottbaron(a)gmail.com'
+ 106760: 'ebaron(a)fedoraproject.org',
+ # Thomas Spura: 'spurath(a)students.uni-mainz.de'
+ 111433: 'tomspur(a)fedoraproject.org',
+ # Adam Miller: 'maxamillion(a)gmail.com'
+ 110673: 'maxamillion(a)fedoraproject.org',
+ # Garrett Holmstrom: 'garrett.holmstrom(a)gmail.com'
+ 131739: 'gholms(a)fedoraproject.org',
+ # Tareq Al Jurf: taljurf.fedora(a)gmail.com
+ 109863: 'taljurf(a)fedoraproject.org',
+ # Josh Kayse: jokajak(a)gmail.com
+ 148243: 'jokajak(a)fedoraproject.org',
+ # Behdad Esfahbod: fedora(a)behdad.org
+ 100102: 'behdad(a)fedoraproject.org',
+ # Daniel Bruno: danielbrunos(a)gmail.com
+ 101608: 'dbruno(a)fedoraproject.org',
+ # Beth Lynn Eicher: bethlynneicher(a)gmail.com
+ 148706: 'bethlynn(a)fedoraproject.org',
+ # Andre Robatino: andre.robatino(a)verizon.net
+ 114970: 'robatino(a)fedoraproject.org',
+ # Jeff Sheltren: jeff(a)tag1consulting.com
+ 100058: 'sheltren(a)fedoraproject.org',
+ # Josh Boyer: jwboyer(a)gmail.com
+ 100115: 'jwboyer(a)redhat.com',
+ }
+ # A few people have an email account that is used in owners.list but
+ # have setup a bugzilla account for their primary account system email
+ # address now. Map these here.
+ self.__alternate_email = {
+ # Damien Durand: splinux25(a)gmail.com
+ 'splinux(a)fedoraproject.org': 100406,
+ # Kevin Fenzi: kevin(a)tummy.com
+ 'kevin-redhat-bugzilla(a)tummy.com': 100037,
+ }
+ for bugzilla_map in self.__bugzilla_email.items():
+ self.__alternate_email[bugzilla_map[1]] = bugzilla_map[0]
+
+ # We use the two mappings as follows::
+ # When looking up a user by email, use __alternate_email.
+ # When looking up a bugzilla email address use __bugzilla_email.
+ #
+ # This allows us to parse in owners.list and have a value for all the
+ # emails in there while not using the alternate email unless it is
+ # the only option.
+
+ # TODO: Use exceptions properly
+
+ ### Set insecure properly ###
+ # When setting insecure, we have to set it both on ourselves and on
+ # self.proxy
+ def _get_insecure(self):
+ return self._insecure
+
+ def _set_insecure(self, insecure):
+ self._insecure = insecure
+ self.proxy = FasProxyClient(self.base_url, useragent=self.useragent,
+ session_as_cookie=False, debug=self.debug, insecure=insecure)
+ return insecure
+ #: If this attribute is set to True, do not check server certificates
+ #: against their CA's. This means that man-in-the-middle attacks are
+ #: possible. You might turn this option on for testing against a local
+ #: version of a server with a self-signed certificate but it should be off
+ #: in production.
+ insecure = property(_get_insecure, _set_insecure)
+
+ ### Groups ###
+
+ def create_group(self, name, display_name, owner, group_type,
+ invite_only=0, needs_sponsor=0, user_can_remove=1,
+ prerequisite='', joinmsg='',
apply_rules='None'):
+ '''Creates a FAS group.
+
+ :arg name: The short group name (alphanumeric only).
+ :arg display_name: A longer version of the group's name.
+ :arg owner: The username of the FAS account which owns the new group.
+ :arg group_type: The kind of group being created. Current valid options
+ are git, svn, hg, shell, and tracking.
+ :kwarg invite_only: Users must be invited to the group, they cannot
+ join on their own.
+ :kwarg needs_sponsor: Users must be sponsored into the group.
+ :kwarg user_can_remove: Users can remove themselves from the group.
+ :kwarg prerequisite: Users must be in the given group (string) before
+ they can join the new group.
+ :kwarg joinmsg: A message shown to users when they apply to the group.
+ :kwarg apply_rules: Rules for applying to the group, shown to users
+ before they apply.
+ :rtype: :obj:`bunch.Bunch`
+ :returns: A Bunch containing information about the group that was
+ created.
+
+ .. versionadded:: 0.3.29
+ '''
+ req_params = {
+ 'invite_only': invite_only,
+ 'needs_sponsor': needs_sponsor,
+ 'user_can_remove': user_can_remove,
+ 'prerequisite': prerequisite,
+ 'joinmsg': joinmsg,
+ 'apply_rules': apply_rules
+ }
+
+ request = self.send_request('/group/create/%s/%s/%s/%s' % (
+ urllib.quote(name),
+ urllib.quote(display_name),
+ urllib.quote(owner),
+ urllib.quote(group_type)),
+ req_params=req_params,
+ auth=True)
+ return request
+
+
+ def group_by_id(self, group_id):
+ '''Returns a group object based on its id'''
+ params = {'group_id': int(group_id)}
+ request = self.send_request('json/group_by_id', auth = True,
+ req_params = params)
+ if request['success']:
+ return request['group']
+ else:
+ return dict()
+
+ def group_by_name(self, groupname):
+ '''Returns a group object based on its name'''
+ params = {'groupname': groupname}
+ request = self.send_request('json/group_by_name', auth = True,
+ req_params = params)
+ if request['success']:
+ return request['group']
+ else:
+ raise AppError(message=b_('FAS server unable to retrieve group'
+ ' %(group)s') % {'group': to_bytes(groupname)},
+ name='FASError')
+
+ def group_members(self, groupname):
+ '''Return a list of people approved for a group.
+
+ This method returns a list of people who are in the requested group.
+ The people are all approved in the group. Unapproved people are not
+ shown. The format of data is::
+
+ \[{'username': 'person1', 'role_type':
'user'},
+ \{'username': 'person2', 'role_type':
'sponsor'}]
+
+ role_type can be one of 'user', 'sponsor', or
'administrator'.
+
+ .. versionadded:: 0.3.2
+ .. versionchanged:: 0.3.21
+ Return a Bunch instead of a DictContainer
+ '''
+ request = self.send_request('/group/dump/%s' %
+ urllib.quote(groupname), auth=True)
+
+ return [Bunch(username=user[0], role_type=user[3])
+ for user in request['people']]
+
+ ### People ###
+
+ def person_by_id(self, person_id):
+ '''Returns a person object based on its id'''
+ person_id = int(person_id)
+ params = {'person_id': person_id}
+ request = self.send_request('json/person_by_id', auth=True,
+ req_params=params)
+
+ if request['success']:
+ if person_id in self.__bugzilla_email:
+ request['person']['bugzilla_email'] = \
+ self.__bugzilla_email[person_id]
+ else:
+ request['person']['bugzilla_email'] =
request['person']['email']
+ # In a devel version of FAS, membership info was returned separately
+ # This was later corrected (can remove this code at some point)
+ if 'approved' in request:
+ request['person']['approved_memberships'] =
request['approved']
+ if 'unapproved' in request:
+ request['person']['unapproved_memberships'] = \
+ request['unapproved']
+ return request['person']
+ else:
+ return dict()
+
+ def person_by_username(self, username):
+ '''Returns a person object based on its username'''
+ params = {'username': username}
+ request = self.send_request('json/person_by_username', auth = True,
+ req_params = params)
+
+ if request['success']:
+ person = request['person']
+ if person['id'] in self.__bugzilla_email:
+ person['bugzilla_email'] =
self.__bugzilla_email[person['id']]
+ else:
+ person['bugzilla_email'] = person['email']
+ # In a devel version of FAS, membership info was returned separately
+ # This was later corrected (can remove this code at some point)
+ if 'approved' in request:
+ request['person']['approved_memberships'] =
request['approved']
+ if 'unapproved' in request:
+ request['person']['unapproved_memberships'] = \
+ request['unapproved']
+ return person
+ else:
+ return dict()
+
+ def gravatar_url(self, username, size=64,
+ default=None):
+ ''' Returns a URL to a gravatar for a given username.
+
+ :arg username: FAS username to construct a gravatar url for
+ :kwarg size: size of the gravatar. Allowed sizes are 32, 64, 140.
+ Default: 64
+ :kwarg default: If gravatar does not have a gravatar image for the
+ email address, this url is returned instead. Default:
+ the fedora logo at the specified size.
+ :raises ValueError: if the size parameter is not allowed
+ :rtype: :obj:`str`
+ :returns: url of a gravatar for the user
+
+ If that user has no gravatar entry, instruct
gravatar.com to redirect
+ us to the Fedora logo.
+
+ If that user has no email attribute, then make a fake request to
+ gravatar.
+
+ .. versionadded:: 0.3.26
+ '''
+ if size not in self._valid_gravatar_sizes:
+ raise ValueError(b_('Size %(size)i disallowed. Must be in'
+ ' %(valid_sizes)r') % { 'size': size,
+ 'valid_sizes': self._valid_gravatar_sizes})
+
+ if not default:
+ default = "http://fedoraproject.org/static/images/" + \
+ "fedora_infinity_%ix%i.png" % (size, size)
+
+ query_string = urllib.urlencode({
+ 's': size,
+ 'd': default,
+ })
+
+ person = self.person_by_username(username)
+ email = person.get('email', 'no_email')
+ hash = md5(email).hexdigest()
+
+ return "http://www.gravatar.com/avatar/%s?%s" % (hash, query_string)
+
+ def user_id(self):
+ '''Returns a dict relating user IDs to usernames'''
+ request = self.send_request('json/user_id', auth=True)
+ people = {}
+ for person_id, username in request['people'].items():
+ # change userids from string back to integer
+ people[int(person_id)] = username
+ return people
+
+ def people_by_key(self, key=u'username', search=u'*', fields=None):
+ '''Return a dict of people
+
+ :kwarg key: Key by this field. Valid values are 'id',
'username', or
+ 'email'. Default is 'username'
+ :kwarg search: Pattern to match usernames against. Defaults to the
+ '*' wildcard which matches everyone.
+ :kwarg fields: Limit the data returned to a specific list of fields.
+ The default is to retrieve all fields.
+ Valid fields are:
+
+ * affiliation
+ * alias_enabled
+ * bugzilla_email
+ * certificate_serial
+ * comments
+ * country_code
+ * creation
+ * email
+ * emailtoken
+ * facsimile
+ * gpg_keyid
+ * group_roles
+ * human_name
+ * id
+ * internal_comments
+ * ircnick
+ * last_seen
+ * latitude
+ * locale
+ * longitude
+ * memberships
+ * old_password
+ * password
+ * password_changed
+ * passwordtoken
+ * postal_address
+ * privacy
+ * roles
+ * ssh_key
+ * status
+ * status_change
+ * telephone
+ * timezone
+ * unverified_email
+ * username
+
+ Note that for most users who access this data, many of these
+ fields will be set to None due to security or privacy settings.
+ :returns: a dict relating the key value to the fields.
+
+ .. versionchanged:: 0.3.21
+ Return a Bunch instead of a DictContainer
+ .. versionchanged:: 0.3.26
+ Fixed to return a list with both people who have signed the CLA
+ and have not
+ '''
+ # Make sure we have a valid key value
+ if key not in ('id', 'username', 'email'):
+ raise KeyError(b_('key must be one of "id",
"username", or'
+ ' "email"'))
+
+ if fields:
+ fields = list(fields)
+ for field in fields:
+ if field not in USERFIELDS:
+ raise KeyError(b_('%(field)s is not a valid field to'
+ ' filter') % {'field': to_bytes(field)})
+ else:
+ fields = USERFIELDS
+
+ # Make sure we retrieve the key value
+ unrequested_fields = []
+ if key not in fields:
+ unrequested_fields.append(key)
+ fields.append(key)
+ if 'bugzilla_email' in fields:
+ # Need id and email for the bugzilla information
+ if 'id' not in fields:
+ unrequested_fields.append('id')
+ fields.append('id')
+ if 'email' not in fields:
+ unrequested_fields.append('email')
+ fields.append('email')
+
+ request = self.send_request('/user/list?tg_format=json',
req_params={'search': search,
+ 'fields': [f for f in fields if f != 'bugzilla_email']},
auth=True)
+
+ people = Bunch()
+ for person in itertools.chain(request['people'],
+ request['unapproved_people']):
+ # Retrieve bugzilla_email from our list if necessary
+ if 'bugzilla_email' in fields:
+ if person['id'] in self.__bugzilla_email:
+ person['bugzilla_email'] = \
+ self.__bugzilla_email[person['id']]
+ else:
+ person['bugzilla_email'] = person['email']
+
+ person_key = person[key]
+ # Remove any fields that weren't requested by the user
+ if unrequested_fields:
+ for field in unrequested_fields:
+ del person[field]
+
+ # Add the person record to the people dict
+ people[person_key] = person
+
+ return people
+
+ def people_by_id(self):
+ '''*Deprecated* Use people_by_key() instead.
+
+ Returns a dict relating user IDs to human_name, email, username,
+ and bugzilla email
+
+ .. versionchanged:: 0.3.21
+ Return a Bunch instead of a DictContainer
+ '''
+ warnings.warn(b_("people_by_id() is deprecated and will be removed in"
+ " 0.4. Please port your code to use
people_by_key(key='id',"
+ " fields=['human_name', 'email', 'username',
'bugzilla_email'])"
+ " instead"), DeprecationWarning, stacklevel=2)
+
+ request = self.send_request('/json/user_id', auth=True)
+ user_to_id = {}
+ people = Bunch()
+ for person_id, username in request['people'].items():
+ person_id = int(person_id)
+ # change userids from string back to integer
+ people[person_id] = {'username': username, 'id': person_id}
+ user_to_id[username] = person_id
+
+ # Retrieve further useful information about the users
+ request = self.send_request('/group/dump', auth=True)
+ for user in request['people']:
+ userid = user_to_id[user[0]]
+ person = people[userid]
+ person['email'] = user[1]
+ person['human_name'] = user[2]
+ if userid in self.__bugzilla_email:
+ person['bugzilla_email'] = self.__bugzilla_email[userid]
+ else:
+ person['bugzilla_email'] = person['email']
+
+ return people
+
+ ### Utils ###
+
+ def people_by_groupname(self, groupname):
+ '''Return a list of persons for the given groupname.
+
+ :arg groupname: Name of the group to look up
+ :returns: A list of person objects from the group. If the group
+ contains no entries, then an empty list is returned.
+ '''
+ people = self.people_by_id()
+ group = dict(self.group_by_name(groupname))
+ userids = [user[u'person_id'] for user in
+ group[u'approved_roles'] +
group[u'unapproved_roles']]
+ return [people[userid] for userid in userids]
+
+ ### Configs ###
+
+ def get_config(self, username, application, attribute):
+ '''Return the config entry for the key values.
+
+ :arg username: Username of the person
+ :arg application: Application for which the config is set
+ :arg attribute: Attribute key to lookup
+ :raises AppError: if the server returns an exception
+ :returns: The unicode string that describes the value. If no entry
+ matched the username, application, and attribute then None is
+ returned.
+ '''
+ request = self.send_request('config/list/%s/%s/%s' %
+ (username, application, attribute), auth=True)
+ if 'exc' in request:
+ raise AppError(name = request['exc'], message =
request['tg_flash'])
+
+ # Return the value if it exists, else None.
+ if 'configs' in request and attribute in request['configs']:
+ return request['configs'][attribute]
+ return None
+
+ def get_configs_like(self, username, application, pattern=u'*'):
+ '''Return the config entries that match the keys and the pattern.
+
+ Note: authentication on the server will prevent anyone but the user
+ or a fas admin from viewing or changing their configs.
+
+ :arg username: Username of the person
+ :arg application: Application for which the config is set
+ :kwarg pattern: A pattern to select values for. This accepts * as a
+ wildcard character. Default='*'
+ :raises AppError: if the server returns an exception
+ :returns: A dict mapping ``attribute`` to ``value``.
+ '''
+ request = self.send_request('config/list/%s/%s/%s' %
+ (username, application, pattern), auth=True)
+ if 'exc' in request:
+ raise AppError(name = request['exc'], message =
request['tg_flash'])
+
+ return request['configs']
+
+ def set_config(self, username, application, attribute, value):
+ '''Set a config entry in FAS for the user.
+
+ Note: authentication on the server will prevent anyone but the user
+ or a fas admin from viewing or changing their configs.
+
+ :arg username: Username of the person
+ :arg application: Application for which the config is set
+ :arg attribute: The name of the config key that we're setting
+ :arg value: The value to set this to
+ :raises AppError: if the server returns an exception
+ '''
+ request = self.send_request('config/set/%s/%s/%s' %
+ (username, application, attribute),
+ req_params={'value': value}, auth=True)
+
+ if 'exc' in request:
+ raise AppError(name = request['exc'], message =
request['tg_flash'])
+
+ def people_query(self, constraints=None, columns=None):
+ '''Returns a list of dicts representing database rows
+
+ :arg constraints: A dictionary specifying WHERE constraints on columns
+ :arg columns: A list of columns to be selected in the query
+ :raises AppError: if the query failed on the server (most likely
+ because the server was given a bad query)
+ :returns: A list of dicts representing database rows (the keys of
+ the dict are the columns requested)
+
+ .. versionadded:: 0.3.12.1
+ '''
+ if constraints is None:
+ constraints = {}
+ if columns is None:
+ columns = []
+
+ req_params = {}
+ req_params.update(constraints)
+ req_params['columns'] = ','.join(columns)
+
+ try:
+ request = self.send_request('json/people_query',
+ req_params=req_params, auth=True)
+ if request['success']:
+ return request['data']
+ else:
+ raise AppError(message=request['error'],
name='FASError')
+ except FedoraServiceError:
+ raise
+
+ ### Certs ###
+
+ def user_gencert(self):
+ '''Generate a cert for a user'''
+ try:
+ request = self.send_request('user/dogencert', auth=True)
+ except FedoraServiceError:
+ raise
+ if not request['cla']:
+ raise CLAError
+ return "%(cert)s\n%(key)s" % request
+
+ ### Passwords ###
+
+ def verify_password(self, username, password):
+ '''Return whether the username and password pair are valid.
+
+ :arg username: username to try authenticating
+ :arg password: password for the user
+ :returns: True if the username/password are valid. False otherwise.
+ '''
+ return self.proxy.verify_password(username, password)
+
+ ### fasClient Special Methods ###
+
+ def group_data(self, group_name='*', force_refresh=None):
+ '''Return administrators/sponsors/users and group type for all
groups
+
+ :arg force_refresh: If true, the returned data will be queried from the
+ database, as opposed to memcached.
+ :raises AppError: if the query failed on the server
+ :returns: A dict mapping group names to the group type and the
+ user IDs of the administrator, sponsors, and users of the group.
+
+ .. versionadded:: 0.3.8
+ '''
+ params = {}
+ if force_refresh:
+ params['force_refresh'] = True
+
+ try:
+ #request = self.send_request('json/fas_client/group_data',
+ request = self.send_request('group/list/%s?tg_format=json' %
group_name,
+ req_params=params, auth=True)
+ return request
+ #if request['success']:
+ # return request['data']
+ #else:
+ # raise AppError(message=b_('FAS server unable to retrieve'
+ # ' group members'), name='FASError')
+ except FedoraServiceError:
+ raise
+
+ def user_data(self, user_key='*'):
+ '''Return user data for all users in FAS
+
+ Note: If the user is not authorized to see password hashes,
+ '*' is returned for the hash.
+
+ :raises AppError: if the query failed on the server
+ :returns: A dict mapping user IDs to a username, password hash,
+ SSH public key, email address, and status.
+
+ .. versionadded:: 0.3.8
+ '''
+ try:
+ request = self.send_request('user/list/%s?tg_format=json' % user_key,
auth=True)
+ return request
+ # if request['success']:
+ # return request['data']
+ # else:
+ # raise AppError(message=b_('FAS server unable to retrieve user'
+ # ' information'), name='FASError')
+ except FedoraServiceError:
+ raise
+
diff --git a/modules/fas/files/hotfix/fasClient b/modules/fas/files/hotfix/fasClient
new file mode 100755
index 0000000..0c1ec7c
--- /dev/null
+++ b/modules/fas/files/hotfix/fasClient
@@ -0,0 +1,629 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright © 2007-2008 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use, modify,
+# copy, or redistribute it subject to the terms and conditions of the GNU
+# General Public License v.2. This program is distributed in the hope that it
+# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
+# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the GNU General Public License for more details. You should have
+# received a copy of the GNU General Public License along with this program;
+# if not, write to the Free Software Foundation, Inc., 51 Franklin Street,
+# Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat trademarks that are
+# incorporated in the source code or documentation are not subject to the GNU
+# General Public License and may only be used or replicated with the express
+# permission of Red Hat, Inc.
+#
+# Red Hat Author(s): Mike McGrath <mmcgrath(a)redhat.com>
+#
+# TODO: put tmp files in a 700 tmp dir
+
+import sys
+import logging
+import syslog
+import os
+import tempfile
+import codecs
+import datetime
+import time
+
+from urllib2 import URLError
+try:
+ #from fedora.client import BaseClient, AuthError, ServerError
+ from fedora.client import AccountSystem, AuthError, ServerError
+except ImportError:
+ from fedora.tg.client import BaseClient, AuthError, ServerError
+from optparse import OptionParser
+from shutil import move, rmtree, copytree
+#from rhpl.translate import _
+
+import gettext
+import locale
+try:
+ locale.setlocale(locale.LC_ALL, '')
+except locale.Error:
+ locale.setlocale(locale.LC_ALL, 'C')
+if os.path.isdir('po'):
+ t = gettext.translation('fas', 'po', fallback = True)
+else:
+ t = gettext.translation('fas', '/usr/share/locale', fallback = True)
+
+_ = t.gettext
+
+import ConfigParser
+
+parser = OptionParser()
+
+parser.add_option('-i', '--install',
+ dest = 'install',
+ default = False,
+ action = 'store_true',
+ help = _('Download and sync most recent content'))
+parser.add_option('-c', '--config',
+ dest = 'CONFIG_FILE',
+ default = '/etc/fas.conf',
+ metavar = 'CONFIG_FILE',
+ help = _('Specify config file (default
"%default")'))
+parser.add_option('--nogroup',
+ dest = 'no_group',
+ default = False,
+ action = 'store_true',
+ help = _('Do not sync group information'))
+parser.add_option('--nopasswd',
+ dest = 'no_passwd',
+ default = False,
+ action = 'store_true',
+ help = _('Do not sync passwd information'))
+parser.add_option('--noshadow',
+ dest = 'no_shadow',
+ default = False,
+ action = 'store_true',
+ help = _('Do not sync shadow information'))
+parser.add_option('--nohome',
+ dest = 'no_home_dirs',
+ default = False,
+ action = 'store_true',
+ help = _('Do not create home dirs'))
+parser.add_option('--nossh',
+ dest = 'no_ssh_keys',
+ default = False,
+ action = 'store_true',
+ help = _('Do not create ssh keys'))
+parser.add_option('-s', '--server',
+ dest = 'FAS_URL',
+ default = None,
+ metavar = 'FAS_URL',
+ help = _('Specify URL of fas server.'))
+parser.add_option('-f', '--force-refresh',
+ dest = 'force_refresh',
+ default = False,
+ action = 'store_true',
+ help = _('Fetch FAS data from the database, not cache'))
+parser.add_option('-p', '--prefix',
+ dest = 'prefix',
+ default = None,
+ metavar = 'prefix',
+ help = _('Specify install prefix. Useful for testing'))
+parser.add_option('-e', '--enable',
+ dest = 'enable',
+ default = False,
+ action = 'store_true',
+ help = _('Enable FAS synced shell accounts'))
+parser.add_option('-d', '--disable',
+ dest = 'disable',
+ default = False,
+ action = 'store_true',
+ help = _('Disable FAS synced shell accounts'))
+parser.add_option('-a', '--aliases',
+ dest = 'aliases',
+ default = False,
+ action = 'store_true',
+ help = _('Sync mail aliases'))
+parser.add_option('--debug',
+ dest = 'debug',
+ default = False,
+ action = 'store_true',
+ help = _('Enable debugging messages'))
+
+
+(opts, args) = parser.parse_args()
+
+log = logging.getLogger('fas')
+
+try:
+ config = ConfigParser.ConfigParser()
+ if os.path.exists(opts.CONFIG_FILE):
+ config.read(opts.CONFIG_FILE)
+ elif os.path.exists('fas.conf'):
+ config.read('fas.conf')
+ print >> sys.stderr, "Could not open %s, defaulting to
./fas.conf" % opts.CONFIG_FILE
+ else:
+ print >> sys.stderr, "Could not open %s." % opts.CONFIG_FILE
+ sys.exit(5)
+except ConfigParser.MissingSectionHeaderError, e:
+ print >> sys.stderr, "Config file does not have proper formatting -
%s" % e
+ sys.exit(6)
+
+FAS_URL = config.get('global', 'url').strip('"')
+if opts.prefix:
+ prefix = opts.prefix
+else:
+ prefix = config.get('global', 'prefix').strip('"')
+
+def _chown(arg, dir_name, files):
+ os.chown(dir_name, arg[0], arg[1])
+ for file in files:
+ os.chown(os.path.join(dir_name, file), arg[0], arg[1])
+
+class MakeShellAccounts(AccountSystem):
+ temp = None
+ groups = None
+ people = None
+ memberships = None
+ emails = None
+ group_mapping = {}
+ valid_groups = {}
+ usernames = {}
+
+ def __init__(self, *args, **kwargs):
+ self._orig_euid = os.geteuid()
+ self._orig_egid = os.getegid()
+ self._orig_groups = os.getgroups()
+
+ force_refresh = kwargs.get('force_refresh')
+ if force_refresh is None:
+ self.force_refresh = False
+ else:
+ del(kwargs['force_refresh'])
+ self.force_refresh = force_refresh
+ super(MakeShellAccounts, self).__init__(*args, **kwargs)
+
+ def mk_tempdir(self):
+ self.temp = tempfile.mkdtemp('-tmp', 'fas-', os.path.join(prefix
+ config.get('global', 'temp').strip('"')))
+ #data = self.send_request('user/list', auth=True, input=params)
+ data = self.user_data()
+
+ def rm_tempdir(self):
+ rmtree(self.temp)
+
+
+ def valid_groups(self):
+ ''' Create a dict of valid groups, including that of group_type
'''
+ if not self.groups:
+ self.group_list()
+ valid_groups = {'groups':[], 'restricted_groups':[],
'ssh_restricted_groups': []}
+ for restriction in valid_groups:
+ for group in config.get('host',
restriction).strip('"').split(','):
+ if group == '':
+ continue
+ if group == '@all':
+ for grp in self.groups:
+ if not grp['name'].startswith('cla'):
+ valid_groups[restriction].append(grp['name'])
+ elif group.startswith('@'):
+ for grp in self.groups:
+ if grp['group_type'] == group[1:]:
+ valid_groups[restriction].append(grp['name'])
+ else:
+ valid_groups[restriction].append(group)
+ self.valid_groups = valid_groups
+
+ def valid_group(self, name, restriction=None):
+ ''' Determine if group is valid on the system '''
+ if restriction:
+ return name in self.valid_groups[restriction]
+ else:
+ for restrict_key in self.valid_groups:
+ if name in self.valid_groups[restrict_key]:
+ return True
+ return False
+
+ def valid_user(self, username):
+ ''' Is the user valid on this system '''
+ if not self.valid_groups:
+ self.valid_groups()
+ if not self.group_mapping:
+ self.get_group_mapping()
+ try:
+ for restriction in self.valid_groups:
+ for group in self.valid_groups[restriction]:
+ if username in self.group_mapping[group]:
+ return True
+ except KeyError:
+ return False
+ return False
+
+ def ssh_key(self, person):
+ ''' determine what ssh key a user should have '''
+ for group in self.valid_groups['groups']:
+ try:
+ if person['username'] in self.group_mapping[group]:
+ return person['ssh_key']
+ except KeyError:
+ print >> sys.stderr, '%s could not be found in fas but was in
your config under "groups"!' % group
+ continue
+ for group in self.valid_groups['restricted_groups']:
+ try:
+ if person['username'] in self.group_mapping[group]:
+ return person['ssh_key']
+ except KeyError:
+ print >> sys.stderr, '%s could not be found in fas but was in
your config under "restricted_groups"!' % group
+ continue
+ for group in self.valid_groups['ssh_restricted_groups']:
+ try:
+ if person['username'] in self.group_mapping[group]:
+ command = config.get('users',
'ssh_restricted_app').strip('"')
+ options = config.get('users',
'ssh_key_options').strip('"')
+ key = 'command="%s",%s %s' % (command, options,
person['ssh_key'])
+ return key
+ except TypeError:
+ print >> sys.stderr, '%s could not be found in fas but was in
your config under "ssh_restricted_groups"!' % group
+ continue
+ return 'INVALID\n'
+
+ def shell(self, username):
+ ''' Determine what shell username should have '''
+ for group in self.valid_groups['groups']:
+ try:
+ if username in self.group_mapping[group]:
+ return config.get('users',
'shell').strip('"')
+ except KeyError:
+ print >> sys.stderr, '%s could not be found in fas but was in
your config under "groups"!' % group
+ continue
+ for group in self.valid_groups['restricted_groups']:
+ try:
+ if username in self.group_mapping[group]:
+ return config.get('users',
'restricted_shell').strip('"')
+ except KeyError:
+ print >> sys.stderr, '%s could not be found in fas but was in
your config under "restricted_groups"!' % group
+ continue
+ for group in self.valid_groups['ssh_restricted_groups']:
+ try:
+ if username in self.group_mapping[group]:
+ return config.get('users',
'ssh_restricted_shell').strip('"')
+ except KeyError:
+ print >> sys.stderr, '%s could not be found in fas but was in
your config under "ssh_restricted_groups"!' % group
+ continue
+
+ print >> sys.stderr, 'Could not determine shell for %s. Defaulting to
/sbin/nologin' % username
+ return '/sbin/nologin'
+
+ def install_aliases_txt(self):
+ move(self.temp + '/aliases', prefix + '/etc/aliases')
+ os.system("newaliases > /dev/null")
+
+ def passwd_text(self, people=None):
+ i = 0
+ passwd_file = codecs.open(self.temp + '/passwd.txt', mode='w',
encoding='utf-8')
+ shadow_file = codecs.open(self.temp + '/shadow.txt', mode='w',
encoding='utf-8')
+ os.chmod(self.temp + '/shadow.txt', 00400)
+ if not self.people:
+ self.people_list()
+ for person in self.people:
+ username = person['username']
+ if self.valid_user(username):
+ uid = person['id']
+ human_name = person['human_name']
+ password = person['password']
+ home_dir = "%s/%s" % (config.get('users',
'home').strip('"'), username)
+ shell = self.shell(username)
+ passwd_file.write("=%s %s:x:%i:%i:%s:%s:%s\n" % (uid, username,
uid, uid, human_name, home_dir, shell))
+ passwd_file.write("0%i %s:x:%i:%i:%s:%s:%s\n" % (i, username,
uid, uid, human_name, home_dir, shell))
+ passwd_file.write(".%s %s:x:%i:%i:%s:%s:%s\n" % (username,
username, uid, uid, human_name, home_dir, shell))
+ shadow_file.write("=%i %s:%s:99999:0:99999:7:::\n" % (uid,
username, password))
+ shadow_file.write("0%i %s:%s:99999:0:99999:7:::\n" % (i,
username, password))
+ shadow_file.write(".%s %s:%s:99999:0:99999:7:::\n" % (username,
username, password))
+ i = i + 1
+ passwd_file.close()
+ shadow_file.close()
+
+ def valid_user_group(self, person_id):
+ ''' Determine if person is valid on this machine as defined in the
+ config file. I worry that this is going to be horribly inefficient
+ with large numbers of users and groups.'''
+ for member in self.memberships:
+ for group in self.memberships[member]:
+ if group['person_id'] == person_id:
+ return True
+ return False
+
+ def get_usernames(self):
+ usernames = {}
+ if not self.people:
+ self.people_list()
+ for person in self.people:
+ uid = person['id']
+ if self.valid_user_group(uid):
+ username = person['username']
+ usernames[uid] = username
+ self.usernames = usernames
+
+ def get_group_mapping(self):
+ if not self.usernames:
+ self.get_usernames()
+ for group in self.groups:
+ gid = group['id']
+ name = group['name']
+ try:
+ ''' Shoot me now I know this isn't right '''
+ members = []
+ for member in self.memberships[name]:
+ members.append(self.usernames[member['person_id']])
+ memberships = ','.join(members)
+ self.group_mapping[name] = members
+ except KeyError:
+ ''' No users exist in the group '''
+ pass
+
+
+ def groups_text(self, groups=None, people=None):
+ i = 0
+ file = open(self.temp + '/group.txt', 'w')
+ if not self.groups:
+ self.group_list()
+ if not self.people:
+ self.people_list()
+ if not self.usernames:
+ self.get_usernames()
+ if not self.group_mapping:
+ self.get_group_mapping()
+ ''' First create all of our users/groups combo '''
+ for person in self.people:
+ uid = person['id']
+ try:
+ if self.valid_user(self.usernames[uid]):
+ username = person['username']
+ file.write("=%i %s:x:%i:\n" % (uid, username, uid))
+ file.write("0%i %s:x:%i:\n" % (i, username, uid))
+ file.write(".%s %s:x:%i:\n" % (username, username, uid))
+ i = i + 1
+ except KeyError:
+ continue
+
+ for group in self.groups:
+ gid = group['id']
+ name = group['name']
+ try:
+ ''' Shoot me now I know this isn't right '''
+ members = []
+ for member in self.memberships[name]:
+ members.append(self.usernames[member['person_id']])
+ memberships = ','.join(members)
+ self.group_mapping[name] = members
+ except KeyError:
+ ''' No users exist in the group '''
+ pass
+ file.write("=%i %s:x:%i:%s\n" % (gid, name, gid, memberships))
+ file.write("0%i %s:x:%i:%s\n" % (i, name, gid, memberships))
+ file.write(".%s %s:x:%i:%s\n" % (name, name, gid, memberships))
+ i = i + 1
+ file.close()
+
+ def group_list(self, search='*'):
+ params = {'search' : search}
+ #request = self.send_request('group/list', auth=True, input=params)
+ #request = self.send_request('group/list', req_params=params)
+ request = self.group_data(force_refresh=False)
+ self.groups = request['groups']
+ memberships = {}
+ for group in self.groups:
+ memberships[group['name']] = []
+ try:
+ for member in request['memberships'][u'%s' %
group['id']]:
+ memberships[group['name']].append(member)
+ except KeyError:
+ pass
+ self.memberships = memberships
+ self.valid_groups()
+ return self.groups
+
+ def people_list(self, search='*'):
+ params = {'search' : search}
+ #data = self.send_request('user/list', auth=True, input=params)
+ data = self.user_data()
+ self.people = data['people'] + data['unapproved_people']
+
+ def email_list(self, search='*'):
+ params = {'search' : search}
+ self.emails = self.send_request('user/email_list', auth=True,
input=params)['emails']
+ return self.emails
+
+ def make_group_db(self):
+ self.groups_text()
+ os.system('makedb -o %s/group.db %s/group.txt' % (self.temp, self.temp))
+
+ def make_passwd_db(self):
+ self.passwd_text()
+ os.system('makedb -o %s/passwd.db %s/passwd.txt' % (self.temp,
self.temp))
+ os.system('makedb -o %s/shadow.db %s/shadow.txt' % (self.temp,
self.temp))
+ os.chmod(self.temp + '/shadow.db', 00400)
+
+ def install_passwd_db(self):
+ try:
+ move(self.temp + '/passwd.db', os.path.join(prefix +
'/var/db/passwd.db'))
+ except IOError, e:
+ print "ERROR: Could not write passwd db - %s" % e
+
+ def install_shadow_db(self):
+ try:
+ move(self.temp + '/shadow.db', os.path.join(prefix +
'/var/db/shadow.db'))
+ except IOError, e:
+ print "ERROR: Could not write shadow db - %s" % e
+
+ def install_group_db(self):
+ try:
+ move(self.temp + '/group.db', os.path.join(prefix +
'/var/db/group.db'))
+ except IOError, e:
+ print "ERROR: Could not write group db - %s" % e
+
+ def create_homedirs(self):
+ ''' Create homedirs and home base dir if they do not exist
'''
+ home_base = os.path.join(prefix + config.get('users',
'home').strip('"'))
+ if not os.path.exists(home_base):
+ os.makedirs(home_base, mode=0755)
+ for person in self.people:
+ home_dir = os.path.join(home_base, person['username'])
+ if not os.path.exists(home_dir) and
self.valid_user(person['username']):
+ syslog.syslog('Creating homedir for %s' %
person['username'])
+ copytree('/etc/skel/', home_dir)
+ os.path.walk(home_dir, _chown, [person['id'],
person['id']])
+
+ def remove_stale_homedirs(self):
+ ''' Remove homedirs of users that no longer have access
'''
+ home_base = os.path.join(prefix + config.get('users',
'home').strip('"'))
+ try:
+ home_backup_dir = config.get('users',
'home_backup_dir').strip('"')
+ except ConfigParser.NoOptionError:
+ home_backup_dir = '/var/tmp/'
+ users = os.listdir(home_base)
+ for user in users:
+ if not self.valid_user(user):
+ if not os.path.exists(home_backup_dir):
+ os.makedirs(home_backup_dir)
+ syslog.syslog('Backed up %s to %s' % (user, home_backup_dir))
+ target = '%s-%s' % (user,
time.mktime(datetime.datetime.now().timetuple()))
+ move(os.path.join(home_base, user), os.path.join(prefix +
home_backup_dir, target))
+
+ def create_ssh_keys(self):
+ ''' Create ssh keys '''
+ home_base = prefix + config.get('users',
'home').strip('"')
+ for person in self.people:
+ username = person['username']
+ if self.valid_user(username):
+ ssh_dir = os.path.join(home_base, username, '.ssh')
+ if person['ssh_key']:
+ key = self.ssh_key(person)
+ if not os.path.exists(ssh_dir):
+ os.makedirs(ssh_dir, mode=0700)
+ f = codecs.open(os.path.join(ssh_dir, 'authorized_keys'),
mode='w', encoding='utf-8')
+ f.write(key + '\n')
+ f.close()
+ os.chmod(os.path.join(ssh_dir, 'authorized_keys'), 0600)
+ os.path.walk(ssh_dir, _chown, [person['id'],
person['id']])
+
+ def make_aliases_txt(self):
+ ''' update your mail aliases file '''
+ if not self.groups:
+ groups = self.group_list()
+ if not self.usernames:
+ self.get_usernames()
+
+ self.emails = self.email_list()
+ email_file = codecs.open(self.temp + '/aliases', mode='w',
encoding='utf-8')
+ email_template = codecs.open(config.get('host',
'aliases_template').strip('"'))
+ email_file.write("# Generated by fasClient\n")
+ for line in email_template.readlines():
+ email_file.write(line)
+ sorted = self.emails.keys()
+ sorted.sort()
+ for person in sorted:
+ email_file.write("%s: %s\n" % (person, self.emails[person]))
+ for group in self.groups:
+ name = group['name']
+ members = {}
+ members['members'] = []
+ members['sponsors'] = []
+ members['administrators'] = []
+ for membership in self.memberships[name]:
+ role_type = membership['role_type']
+ person = self.usernames[membership['person_id']]
+ members['members'].append(person)
+ if role_type == 'user':
+ continue
+ members['sponsors'].append(person)
+ if role_type == 'sponsor':
+ continue
+ members['administrators'].append(person)
+ for role in members:
+ email_file.write("%s-%s: %s\n" % (name, role,
','.join(members[role])))
+ email_file.close()
+
+def enable():
+ temp = tempfile.mkdtemp('-tmp', 'fas-', config.get('global',
'temp').strip('"'))
+
+ old = open('/etc/sysconfig/authconfig', 'r')
+ new = open(temp + '/authconfig', 'w')
+ for line in old:
+ if line.startswith("USEDB"):
+ new.write("USEDB=yes\n")
+ else:
+ new.write(line)
+ new.close()
+ old.close()
+ try:
+ move(temp + '/authconfig', '/etc/sysconfig/authconfig')
+ except IOError, e:
+ print "ERROR: Could not write /etc/sysconfig/authconfig - %s" % e
+ sys.exit(5)
+ os.system('/usr/sbin/authconfig --updateall')
+ rmtree(temp)
+
+def disable():
+ temp = tempfile.mkdtemp('-tmp', 'fas-', config.get('global',
'temp').strip('"'))
+ old = open('/etc/sysconfig/authconfig', 'r')
+ new = open(temp + '/authconfig', 'w')
+ for line in old:
+ if line.startswith("USEDB"):
+ new.write("USEDB=no\n")
+ else:
+ new.write(line)
+ old.close()
+ new.close()
+ try:
+ move(temp + '/authconfig', '/etc/sysconfig/authconfig')
+ except IOError, e:
+ print "ERROR: Could not write /etc/sysconfig/authconfig - %s" % e
+ sys.exit(5)
+ os.system('/usr/sbin/authconfig --updateall')
+ rmtree(temp)
+
+
+if __name__ == '__main__':
+ if opts.enable:
+ enable()
+ if opts.disable:
+ disable()
+
+ if opts.install:
+ try:
+ #fas = MakeShellAccounts(FAS_URL, config.get('global',
'login').strip('"'), config.get('global',
'password').strip('"'), opts.debug)
+ fas = MakeShellAccounts(FAS_URL,
+ username=config.get('global',
'login').strip('"'),
+ password=config.get('global',
'password').strip('"'),
+ force_refresh=True, debug=opts.debug)
+ except AuthError, e:
+ print >> sys.stderr, e
+ sys.exit(1)
+ except URLError, e:
+ print >> sys.stderr, 'Could not connect to %s - %s' % (FAS_URL,
e.reason[1])
+ sys.exit(9)
+ fas.mk_tempdir()
+ fas.make_group_db()
+ fas.make_passwd_db()
+ if not opts.no_group:
+ fas.install_group_db()
+ if not opts.no_passwd:
+ fas.install_passwd_db()
+ if not opts.no_shadow:
+ fas.install_shadow_db()
+ if not opts.no_home_dirs:
+ fas.create_homedirs()
+ fas.remove_stale_homedirs()
+ if not opts.no_ssh_keys:
+ fas.create_ssh_keys()
+ fas.rm_tempdir()
+ if opts.aliases:
+ try:
+ fas = MakeShellAccounts(FAS_URL, config.get('global',
'login').strip('"'), config.get('global',
'password').strip('"'), opts.debug)
+ except AuthError, e:
+ print >> sys.stderr, e
+ sys.exit(1)
+ fas.mk_tempdir()
+ fas.make_aliases_txt()
+ fas.install_aliases_txt()
+ fas.rm_tempdir()
+
+ if not (opts.install or opts.enable or opts.disable or opts.aliases):
+ parser.print_help()
diff --git a/modules/fas/manifests/init.pp b/modules/fas/manifests/init.pp
index ebd6ed0..ac7b2f5 100644
--- a/modules/fas/manifests/init.pp
+++ b/modules/fas/manifests/init.pp
@@ -28,7 +28,7 @@ class fas {
class client (
$group = $fas_groups ? { "" => ["sysadmin-main" ], default =>
$fas_groups }
- $restricted_app = $fas_restricted_app ? { "" => ["/usr/bin/cvs
server", default => $fas_restricted_app }
+ $restricted_app = $fas_restricted_app ? { "" => ["/usr/bin/cvs
server"], default => $fas_restricted_app }
$ssh_groups = $fas_ssh_groups
)
{
@@ -42,6 +42,21 @@ class fas {
content => template("fas/client/fas.conf.erb"),
require => Package["fas-clients"],
}
+
+ # BackPort fas-0.8.3.2
+ file { "/usr/bin/fasClient":
+ owner => "root",
+ group => "root",
+ mode => 755,
+ source => "puppet:///fas/hotfix/fasClient",
+ require => Package["python-fedora"],
+ }
+ file { "/usr/lib/python-2.6/site-packages/fedora/client/fas2.py":
+ owner => "root",
+ group => "root",
+ source => "puppet:///fas/hotfix/python-fedora_fas2.py",
+ require => Package["python-fedora"],
+ }
}
}