From 780389dd9c9b13a49c9c50409ae0a8f89048d22c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Toshio=20=E3=81=8F=E3=82=89=E3=81=A8=E3=81=BF?= Date: Wed, 5 Mar 2008 00:10:09 +0000 Subject: [PATCH] Remove jsonfas providers and client.py module. These have been merged back to python-fedora. --- fas/fas/client.py | 228 ------------------------------------ fas/fas/jsonfasprovider.py | 234 ------------------------------------- fas/fas/jsonfasvisit.py | 82 ------------- 3 files changed, 544 deletions(-) delete mode 100644 fas/fas/client.py delete mode 100644 fas/fas/jsonfasprovider.py delete mode 100644 fas/fas/jsonfasvisit.py diff --git a/fas/fas/client.py b/fas/fas/client.py deleted file mode 100644 index 36e18d1..0000000 --- a/fas/fas/client.py +++ /dev/null @@ -1,228 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2007 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): Luke Macken -# Toshio Kuratomi -# - -''' -python-fedora, python module to interact with Fedora Infrastructure Services -''' - -import Cookie -import urllib -import urllib2 -import logging -import cPickle as pickle -import re -import inspect -import simplejson -from os import path -from urlparse import urljoin - -import gettext -t = gettext.translation('python-fedora', '/usr/share/locale', fallback=True) -_ = t.ugettext - -log = logging.getLogger(__name__) - -SESSION_FILE = path.join(path.expanduser('~'), '.fedora_session') - -class ServerError(Exception): - pass - -class AuthError(ServerError): - pass - -class BaseClient(object): - ''' - A command-line client to interact with Fedora TurboGears Apps. - ''' - def __init__(self, baseURL, username=None, password=None, debug=False): - self.baseURL = baseURL - self.username = username - self.password = password - self._sessionCookie = None - - # Setup our logger - sh = logging.StreamHandler() - if debug: - log.setLevel(logging.DEBUG) - sh.setLevel(logging.DEBUG) - else: - log.setLevel(logging.INFO) - sh.setLevel(logging.INFO) - format = logging.Formatter("%(message)s") - sh.setFormatter(format) - log.addHandler(sh) - - self._load_session() - if username and password: - self._authenticate(force=True) - - def _authenticate(self, force=False): - ''' - Return an authenticated session cookie. - ''' - if not force and self._sessionCookie: - return self._sessionCookie - if not self.username: - raise AuthError, _('username must be set') - if not self.password: - raise AuthError, _('password must be set') - - req = urllib2.Request(urljoin(self.baseURL, 'login?tg_format=json')) - req.add_header('Cookie', self._sessionCookie.output(attrs=[], - header='').strip()) - req.add_data(urllib.urlencode({ - 'user_name' : self.username, - 'password' : self.password, - 'login' : 'Login' - })) - - try: - loginPage = urllib2.urlopen(req) - except urllib2.HTTPError, e: - if e.msg == 'Forbidden': - raise AuthError, _('Invalid username/password') - else: - raise - - loginData = simplejson.load(loginPage) - - if 'message' in loginData: - raise AuthError, _('Unable to login to server: %(message)s') \ - % loginData - - self._sessionCookie = Cookie.SimpleCookie() - try: - self._sessionCookie.load(loginPage.headers['set-cookie']) - except KeyError: - self._sessionCookie = None - raise AuthError, _('Unable to login to the server. Server did' \ - ' not send back a cookie') - self._save_session() - - return self._sessionCookie - session = property(_authenticate) - - def _save_session(self): - ''' - Store our pickled session cookie. - - This method loads our existing session file and modified our - current user's cookie. This allows us to retain cookies for - multiple users. - ''' - save = {} - if path.isfile(SESSION_FILE): - sessionFile = file(SESSION_FILE, 'r') - try: - save = pickle.load(sessionFile) - except: - pass - sessionFile.close() - save[self.username] = self._sessionCookie - sessionFile = file(SESSION_FILE, 'w') - pickle.dump(save, sessionFile) - sessionFile.close() - - def _load_session(self): - ''' - Load a stored session cookie. - ''' - if path.isfile(SESSION_FILE): - sessionFile = file(SESSION_FILE, 'r') - try: - savedSession = pickle.load(sessionFile) - self._sessionCookie = savedSession[self.username] - log.debug(_('Loaded session %(cookie)s') % \ - {'cookie': self._sessionCookie}) - except EOFError: - log.error(_('Unable to load session from %(file)s') % \ - {'file': SESSION_FILE}) - except KeyError: - log.debug(_('Session is for a different user')) - sessionFile.close() - - def send_request(self, method, auth=False, input=None): - ''' - Send a request to the server. The given method is called with any - keyword parameters in **kw. If auth is True, then the request is - made with an authenticated session cookie. - ''' - url = urljoin(self.baseURL, method + '?tg_format=json') - - response = None # the JSON that we get back from the server - data = None # decoded JSON via simplejson.load() - - log.debug(_('Creating request %(url)s') % {'url': url}) - req = urllib2.Request(url) - if input: - req.add_data(urllib.urlencode(input)) - - if auth: - req.add_header('Cookie', self.session.output(attrs=[], - header='').strip()) - elif self._sessionCookie: - # If the cookie exists, send it so that visit tracking works. - req.add_header('Cookie', self._sessionCookie.output(attrs=[], - header='').strip()) - try: - response = urllib2.urlopen(req) - except urllib2.HTTPError, e: - if e.msg == 'Forbidden': - if (inspect.currentframe().f_back.f_code != - inspect.currentframe().f_code): - self._authenticate(force=True) - data = self.send_request(method, auth, input) - else: - # We actually shouldn't ever reach here. Unless something - # goes drastically wrong _authenticate should raise an - # AuthError - raise AuthError, _('Unable to log into server: %(error)s') \ - % {'error': str(e)} - log.error(e) - raise ServerError, str(e) - - # In case the server returned a new session cookie to us - try: - self._sessionCookie.load(response.headers['set-cookie']) - except KeyError: - pass - - try: - data = simplejson.load(response) - except Exception, e: - regex = re.compile('(.*)') - match = regex.search(response) - if match and len(match.groups()): - return dict(tg_flash=match.groups()[0]) - else: - raise ServerError, e.message - - if 'logging_in' in data: - if (inspect.currentframe().f_back.f_code != - inspect.currentframe().f_code): - self._authenticate(force=True) - data = self.send_request(method, auth, input) - else: - # We actually shouldn't ever reach here. Unless something goes - # drastically wrong _authenticate should raise an AuthError - raise AuthError, _('Unable to log into server: %(message)s') \ - % data - return data diff --git a/fas/fas/jsonfasprovider.py b/fas/fas/jsonfasprovider.py deleted file mode 100644 index 73c09b6..0000000 --- a/fas/fas/jsonfasprovider.py +++ /dev/null @@ -1,234 +0,0 @@ -# -*- 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): Toshio Kuratomi -# - -''' -This plugin provides integration with the Fedora Account -System using JSON calls. -''' - -import Cookie - -from cherrypy import request -from sqlalchemy.orm import class_mapper -from turbogears import config, identity -from turbogears.identity.saprovider import SqlAlchemyIdentity, \ - SqlAlchemyIdentityProvider -from turbogears.database import session -from turbogears.util import load_class - -# Once this works, propogate the changes back to python-fedora and import as -# from fedora.tg.client import BaseClient -from client import BaseClient - -import gettext -t = gettext.translation('python-fedora', '/usr/share/locale', fallback=True) -_ = t.ugettext - -import crypt - -import logging -log = logging.getLogger('turbogears.identity.safasprovider') - -try: - set, frozenset -except NameError: - from sets import Set as set, ImmutableSet as frozenset - -class JsonFasIdentity(BaseClient): - '''Associate an identity with a person in the auth system. - ''' - cookieName = config.get('visit.cookie.name', 'tg-visit') - fasURL = config.get('fas.url', 'https://admin.fedoraproject.org/admin/fas/') - - def __init__(self, visit_key, user=None, username=None, password=None, - debug=False): - super(JsonFasIdentity, self).__init__(self.fasURL, debug=debug) - if user: - self._user = user - self._groups = frozenset( - [g['name'] for g in data['person']['approved_memberships']] - ) - self.visit_key = visit_key - # It's allowed to use a null value for a visit_key if we know we're - # generating an anonymous user. The json interface doesn't handle - # that, though, and there's no reason for us to make it. - if not visit_key: - return - - # Set the cookie to the user's tg_visit key before requesting - # authentication. That way we link the two together. - self._sessionCookie = Cookie.SimpleCookie() - self._sessionCookie[self.cookieName] = self.visit_key - self.username = username - self.password = password - if username and password: - self._authenticate(force=True) - - def _authenticate(self, force=False): - '''Override BaseClient so we can keep visit_key in sync. - ''' - super(JsonFasIdentity, self)._authenticate(force) - if self._sessionCookie[self.cookieName].value != self.visit_key: - # When the visit_key changes (because the old key had expired or - # been deleted from the db) change the visit_key in our variables - # and the session cookie to be sent back to the client. - self.visit_key = self._sessionCookie[self.cookieName].value - cookies = request.simple_cookie - cookies[self.cookieName] = self.visit_key - return self._sessionCookie - session = property(_authenticate) - - def _get_user(self): - '''Retrieve information about the user from cache or network.''' - try: - return self._user - except AttributeError: - # User hasn't already been set - pass - # Attempt to load the user. After this code executes, there *WILL* be - # a _user attribute, even if the value is None. - # Query the account system URL for our given user's sessionCookie - # FAS returns user and group listing - data = self.send_request('user/view', auth=True) - if not data['person']: - self._user = None - return None - self._user = data['person'] - self._groups = frozenset( - [g['name'] for g in data['person']['approved_memberships']] - ) - return self._user - user = property(_get_user) - - def _get_user_name(self): - if not self.user: - return None - return self.user['username'] - user_name = property(_get_user_name) - - def _get_groups(self): - try: - return self._groups - except AttributeError: - # User and groups haven't been returned. Since the json call - # returns both user and groups, this is set at user creation time. - self._groups = frozenset() - return self._groups - groups = property(_get_groups) - - def logout(self): - ''' - Remove the link between this identity and the visit. - ''' - if not self.visit_key: - return - # Call Account System Server logout method - self.send_request('logout', auth=True) - -class JsonFasIdentityProvider(object): - ''' - IdentityProvider that authenticates users against the fedora account system - ''' - def __init__(self): - # Default encryption algorithm is to use plain text passwords - algorithm = config.get("identity.saprovider.encryption_algorithm", None) - self.encrypt_password = lambda pw: \ - identity._encrypt_password(algorithm, pw) - - def create_provider_model(self): - ''' - Create the database tables if they don't already exist. - ''' - # No database tables to create because the db is behind the FAS2 - # server - pass - - def validate_identity(self, user_name, password, visit_key): - ''' - Look up the identity represented by user_name and determine whether the - password is correct. - - Must return either None if the credentials weren't valid or an object - with the following properties: - user_name: original user name - user: a provider dependant object (TG_User or similar) - groups: a set of group IDs - permissions: a set of permission IDs - ''' - try: - user = JsonFasIdentity(visit_key, username=user_name, - password=password) - except AuthError, e: - log.warning('Error logging in %(user)s: %(error)s' % { - 'user': username, 'error': e}) - return None - - return JsonFasIdentity(visit_key, user) - - def validate_password(self, user, user_name, password): - ''' - Check the supplied user_name and password against existing credentials. - Note: user_name is not used here, but is required by external - password validation schemes that might override this method. - If you use SqlAlchemyIdentityProvider, but want to check the passwords - against an external source (i.e. PAM, LDAP, Windows domain, etc), - subclass SqlAlchemyIdentityProvider, and override this method. - - Arguments: - :user: User information. Not used. - :user_name: Given username. - :password: Given, plaintext password. - - Returns: True if the password matches the username. Otherwise False. - Can return False for problems within the Account System as well. - ''' - - return user.password == crypt.crypt(password, user.password) - - def load_identity(self, visit_key): - '''Lookup the principal represented by visit_key. - - Arguments: - :visit_key: The session key for whom we're looking up an identity. - - Must return an object with the following properties: - user_name: original user name - user: a provider dependant object (TG_User or similar) - groups: a set of group IDs - permissions: a set of permission IDs - ''' - return JsonFasIdentity(visit_key) - - def anonymous_identity(self): - ''' - Must return an object with the following properties: - user_name: original user name - user: a provider dependant object (TG_User or similar) - groups: a set of group IDs - permissions: a set of permission IDs - ''' - - return JsonFasIdentity(None) - - def authenticated_identity(self, user): - ''' - Constructs Identity object for user that has no associated visit_key. - ''' - return JsonFasIdentity(None, user) diff --git a/fas/fas/jsonfasvisit.py b/fas/fas/jsonfasvisit.py deleted file mode 100644 index 0bdd38a..0000000 --- a/fas/fas/jsonfasvisit.py +++ /dev/null @@ -1,82 +0,0 @@ -''' -This plugin provides integration with the Fedora Account System using JSON -calls to the account system server. -''' - -import Cookie - -from datetime import datetime - -from sqlalchemy import * -from sqlalchemy.orm import class_mapper - -from turbogears import config -from turbogears.visit.api import BaseVisitManager, Visit -from turbogears.database import get_engine, metadata, session, mapper -from turbogears.util import load_class - -# Once this works, propogate the changes back to python-fedora and import as -# from fedora.tg.client import BaseClient -from client import BaseClient - -import gettext -t = gettext.translation('python-fedora', '/usr/share/locale', fallback=True) -_ = t.ugettext - -import logging -log = logging.getLogger("turbogears.identity.savisit") - -class JsonFasVisitManager(BaseClient): - ''' - This proxies visit requests to the Account System Server running remotely. - - We don't need to worry about threading and other concerns because our proxy - doesn't cause any asynchronous calls. - ''' - fasURL = config.get('fas.url', 'https://admin.fedoraproject.org/admin/fas') - cookieName = config.get('visit.cookie.name', 'tg-visit') - - def __init__(self, timeout, debug=None): - super(JsonFasVisitManager,self).__init__(self.fasURL, debug=debug) - - def create_model(self): - ''' - Create the Visit table if it doesn't already exist - ''' - # Not needed as the visit tables reside remotely in the FAS2 database. - pass - - def new_visit_with_key(self, visit_key): - # Hit any URL in fas2 with the visit_key set. That will call the - # new_visit method in fas2 - self._sessionCookie = Cookie.SimpleCookie() - self._sessionCookie[self.cookieName] = visit_key - data = self.send_request('', auth=True) - return Visit(self._sessionCookie[self.cookieName].value, True) - - def visit_for_key(self, visit_key): - ''' - Return the visit for this key or None if the visit doesn't exist or has - expired. - ''' - # Hit any URL in fas2 with the visit_key set. That will call the - # new_visit method in fas2 - self._sessionCookie = Cookie.SimpleCookie() - self._sessionCookie[self.cookieName] = visit_key - data = self.send_request('', auth=True) - # Knowing what happens in turbogears/visit/api.py when this is called, - # we can shortcircuit this step and avoid a round trip to the FAS - # server. - # if visit_key != self._sessionCookie[self.cookieName].value: - # # visit has expired - # return None - # # Hitting FAS has already updated the visit. - # return Visit(visit_key, False) - if visit_key != self._sessionCookie[self.cookieName].value: - return Visit(self._sessionCookie[self.cookieName].value, True) - else: - return Visit(visit_key, False) - - def update_queued_visits(self, queue): - # Let the visit_manager on the FAS server manage this - pass