Remove jsonfas providers and client.py module. These have been merged back to

python-fedora.
This commit is contained in:
Toshio くらとみ 2008-03-05 00:10:09 +00:00
parent 9916019c9c
commit 780389dd9c
3 changed files with 0 additions and 544 deletions

View file

@ -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 <lmacken@redhat.com>
# Toshio Kuratomi <tkuratom@redhat.com>
#
'''
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('<span class="fielderror">(.*)</span>')
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

View file

@ -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 <tkuratom@redhat.com>
#
'''
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)

View file

@ -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