Merge branch 'master' of ssh://git.fedorahosted.org/git/fedora-infrastructure

* Fix conflict in controllers.py::logout() method.
This commit is contained in:
Toshio Kuratomi 2008-03-03 07:24:49 -08:00
commit 803d0b5d2f
34 changed files with 2741 additions and 2742 deletions

1
fas/.gitignore vendored
View file

@ -8,4 +8,5 @@ fas.log
*.pyc
*.pyo
*.swp
*.mo
fas.egg-info

View file

@ -27,7 +27,7 @@ Before you can get started, make sure to have the following packages installed
yum install git-core postgresql-plpython postgresql-server postgresql-python \
python-TurboMail TurboGears pygpgme python-sqlalchemy python-genshi \
python-psycopg2
python-psycopg2 pytz
# Note: on RHEL5 you need postgresql-pl instead of postgresql-plpython
@ -123,4 +123,5 @@ To test, look and see if your groups or users show up with getent. For
example:
getent passwd
getent group
getent group

View file

@ -30,7 +30,7 @@ from optparse import OptionParser
from shutil import move, rmtree
from rhpl.translate import _
FAS_URL = 'http://localhost:8080/fas/'
FAS_URL = 'http://localhost:8088/fas/'
parser = OptionParser()
@ -78,20 +78,15 @@ class MakeShellAccounts(BaseClient):
temp = None
def mk_tempdir(self):
self.temp = tempfile.mkdtemp('-tmp', 'fas-')
os.chmod(self.temp, 00400)
self.temp = tempfile.mkdtemp('-tmp', 'fas-', '/var/db')
def rm_tempdir(self):
rmtree(self.temp)
def group_list(self, search='*'):
params = {'search' : search}
data = self.send_request('group/list', auth=True, input=params)
return data
def shadow_text(self, people=None):
i = 0
file = open(self.temp + '/shadow.txt', 'w')
os.chmod(self.temp + '/shadow.txt', 00400)
if not people:
people = self.people_list()
for person in people:
@ -103,8 +98,6 @@ class MakeShellAccounts(BaseClient):
file.write(".%s %s:%s:99999:0:99999:7:::\n" % (username, username, password))
i = i + 1
file.close()
os.chmod(self.temp + '/shadow.txt', 00400)
def passwd_text(self, people=None):
i = 0
@ -136,10 +129,10 @@ class MakeShellAccounts(BaseClient):
for person in people:
uid = person['id']
username = person['username']
usernames['%s' % uid] = username
usernames[uid] = 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))
file.write("0%i %s:x:%i:\n" % (i, username, uid))
file.write(".%s %s:x:%i:\n" % (username, username, uid))
i = i + 1
for group in groups['groups']:
@ -149,18 +142,23 @@ class MakeShellAccounts(BaseClient):
try:
''' Shoot me now I know this isn't right '''
members = []
for member in groups['memberships'][u'%s' % gid]:
members.append(usernames['%s' % member['person_id']])
for member in groups['memberships'][name]:
members.append(usernames[member['person_id']])
memberships = ','.join(members)
except KeyError:
''' No users exist in the group '''
pass
file.write( "=%i %s:x:%i:%s\n" % (gid, name, gid, memberships))
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}
data = self.send_request('group/list', auth=True, input=params)
return data
def people_list(self, search='*'):
params = {'search' : search}
@ -205,14 +203,11 @@ def enable():
if line.startswith('passwd') or line.startswith('shadow') or line.startswith('group'):
parts = line.split()
if 'db' in parts:
new.write(line)
print "%s already has db enabled" % parts[0].split(':')[0]
else:
tmp = line.strip('\n')
tmp = tmp + ' db\n'
new.write(tmp)
else:
new.write(line)
line = line.strip('\n')
line += ' db\n'
new.write(line)
new.close()
try:
move('/tmp/.fas.nsswitch.conf', '/etc/nsswitch.conf')
@ -226,13 +221,10 @@ def disable():
if line.startswith('passwd') or line.startswith('shadow') or line.startswith('group'):
parts = line.split()
if 'db' in parts:
tmp = line.replace(' db', '')
new.write(tmp)
line = line.replace(' db', '')
else:
print "%s already has db disabled" % parts[0].split(':')[0]
new.write(line)
else:
new.write(line)
new.write(line)
new.close()
try:
move('/tmp/.fas.nsswitch.conf', '/etc/nsswitch.conf')
@ -240,13 +232,7 @@ def disable():
print "ERROR: Could not write nsswitch.conf - %s" % e
if __name__ == '__main__':
if opts.enable:
enable()
sys.exit()
elif opts.disable:
disable()
sys.exit()
elif opts.install:
if opts.install:
try:
fas = MakeShellAccounts(FAS_URL, 'admin', 'admin', False)
except AuthError, e:
@ -263,5 +249,9 @@ if __name__ == '__main__':
if not opts.no_shadow:
fas.install_shadow_db()
fas.rm_tempdir()
else:
if opts.enable:
enable()
if opts.disable:
disable()
if not (opts.install or opts.enable or opts.disable):
parser.print_help()

View file

@ -7,6 +7,7 @@
#mail.server = 'bastion.fedora.phx.redhat.com'
#base_url_filter.base_url = "http://192.168.2.101:8080"
fas.url = 'http://localhost:8088/fas/'
mail.on = True
mail.server = 'bastion.fedora.phx.redhat.com'
mail.testmode = True

View file

@ -59,6 +59,8 @@ genshi.encoding="utf-8"
# i18n
session_filter.on = True
i18n.run_template_filter = True
i18n.domain = 'fas'
i18n.locale_dir = 'po'
# VISIT TRACKING
# Each visit to your application will be assigned a unique visit ID tracked via

View file

@ -5,6 +5,7 @@ from cherrypy import request, response
from turbogears import exception_handler
import turbogears
import cherrypy
import time
from fas.user import User
@ -24,6 +25,17 @@ def add_custom_stdvars(vars):
turbogears.view.variable_providers.append(add_custom_stdvars)
def get_locale(locale=None):
if locale:
return locale
if turbogears.identity.current.user_name:
person = People.by_username(turbogears.identity.current.user_name)
return person.locale
else:
return turbogears.i18n.utils._get_locale()
config.update({'i18n.get_locale': get_locale})
# from fas import json
# import logging
# log = logging.getLogger("fas.controllers")
@ -38,7 +50,7 @@ class Root(controllers.RootController):
cla = CLA()
json = JsonRequest()
help = Help()
# openid = OpenID()
#openid = OpenID()
# TODO: Find a better place for this.
os.environ['GNUPGHOME'] = config.get('gpghome')
@ -110,3 +122,11 @@ class Root(controllers.RootController):
# is better.
return dict(status=True)
raise redirect(request.headers.get("Referer", "/"))
@expose()
def language(self, locale):
locale_key = config.get("i18n.session_key", "locale")
cherrypy.session[locale_key] = locale
raise redirect(request.headers.get("Referer", "/"))

View file

@ -1,553 +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.
#
# Author(s): Mike McGrath <mmcgrath@redhat.com>
# Toshio Kuratomi <tkuratom@redhat.com>
# Ricky Zhou <ricky@fedoraproject.org>
#
'''
python-fedora, python module to interact with Fedora Infrastructure Services
'''
import ldap
from ldap import modlist
import time
from random import Random
import sha
from base64 import b64encode
import sys
import os
dbName = 'fastest'
class AuthError(Exception):
pass
def retrieve_db_info(dbKey):
'''Retrieve information to connect to the db from the filesystem.
Arguments:
:dbKey: The string identifying the database entry in the config file.
Returns: A dictionary containing the values to use in connecting to the
database.
Exceptions:
:IOError: Returned if the config file is not on the system.
:AuthError: Returned if there is no record found for dbKey in the
config file.
'''
# Open a filehandle to the config file
if os.environ.has_key('HOME') and os.path.isfile(
os.path.join(os.environ.get('HOME'), '.fedora-db-access')):
fh = file(os.path.join(
os.environ.get('HOME'), '.fedora-db-access'), 'r')
elif os.path.isfile('/etc/sysconfig/fedora-db-access'):
fh = file('/etc/sysconfig/fedora-db-access', 'r')
else:
raise IOError, 'fedora-db-access file does not exist.'
# Read the file until we get the information for the requested db
dbInfo = None
for line in fh.readlines():
if not line:
break
line = line.strip()
if not line or line[0] == '#':
continue
pieces = line.split(None, 1)
if len(pieces) < 2:
continue
if pieces[0] == dbKey:
dbInfo = eval(pieces[1])
break
if fh:
fh.close()
if not dbInfo:
raise AuthError, 'Authentication source "%s" not configured' % (dbKey,)
return dbInfo
class Server(object):
def __init__(self, server=None, who=None, password=None):
pass
#try:
#dbInfo = retrieve_db_info(dbName)
#except IOError:
#raise AuthError, 'Authentication config file fedora-db-access is' \
#' not available'
#server = server or dbInfo['host'] or 'localhost'
#who = 'cn=%s' % (who or dbInfo['user'])
## Some db connections have no password
#password = password or dbInfo.get('password')
#self.ldapConn = ldap.open(server)
#self.ldapConn.simple_bind_s(who, password)
def add(self, base, attributes):
''' Add a new group record to LDAP instance '''
self.ldapConn.add_s(base, attributes.items())
def delete(self, base):
''' Delete target base '''
self.ldapConn.delete_s(base)
def modify(self, base, attribute, new, old=None):
''' Modify an attribute, requires write access '''
if new is None:
return None
new = unicode(new).encode('utf-8')
if new == old:
return None
#o = { attribute : old }
#n = { attribute : new }
ldif = []
ldif.append((ldap.MOD_DELETE,attribute,None))
ldif.append((ldap.MOD_ADD,attribute,new))
#ldif = ldap.modlist.modifyModlist(o, n, ignore_oldexistent=1)
# commit
self.ldapConn.modify_s(base, ldif)
def search(self, base, ldapFilter, attributes=None):
''' Basic search function '''
scope = ldap.SCOPE_SUBTREE
count = 0
timeout = 2
result_set = []
try:
result_id = self.ldapConn.search(base, scope, ldapFilter, attributes)
while True:
result_type, result_data = self.ldapConn.result(result_id, timeout)
if (result_data == []):
break
else:
if result_type == ldap.RES_SEARCH_ENTRY:
result_set.append(result_data)
if len(result_set) == 0:
return
except ldap.LDAPError, e:
raise
return result_set
###############################################################################
# Group - Contains information about a specific group, 'sysadmin' would be
# an example of a Group
###############################################################################
class Group(object):
''' Group abstraction class '''
__server = Server()
__base = 'ou=FedoraGroups,dc=fedoraproject,dc=org'
def __init__(self, cn, fedoraGroupDesc, fedoraGroupOwner, fedoraGroupType, fedoraGroupNeedsSponsor, fedoraGroupUserCanRemove, fedoraGroupRequires, fedoraGroupJoinMsg):
self.cn = cn
self.fedoraGroupDesc = fedoraGroupDesc
self.fedoraGroupOwner = fedoraGroupOwner
self.fedoraGroupType = fedoraGroupType
self.fedoraGroupNeedsSponsor = fedoraGroupNeedsSponsor
self.fedoraGroupUserCanRemove = fedoraGroupUserCanRemove
self.fedoraGroupRequires = fedoraGroupRequires
self.fedoraGroupJoinMsg = fedoraGroupJoinMsg
def __json__(self):
return {'cn': self.cn,
'fedoraGroupDesc': self.fedoraGroupDesc,
'fedoraGroupOwner': self.fedoraGroupOwner,
'fedoraGroupType': self.fedoraGroupType,
'fedoraGroupNeedsSponsor': self.fedoraGroupNeedsSponsor,
'fedoraGroupUserCanRemove': self.fedoraGroupUserCanRemove,
'fedoraGroupRequires': self.fedoraGroupRequires,
'fedoraGroupJoinMsg': self.fedoraGroupJoinMsg
}
@classmethod
def newGroup(self, cn, fedoraGroupDesc, fedoraGroupOwner, fedoraGroupType, fedoraGroupNeedsSponsor, fedoraGroupUserCanRemove, fedoraGroupRequires, fedoraGroupJoinMsg):
''' Create a new group '''
attributes = { 'cn' : cn.encode('utf-8'),
'objectClass' : ('fedoraGroup'),
'fedoraGroupDesc' : fedoraGroupDesc.encode('utf-8'),
'fedoraGroupOwner' : fedoraGroupOwner.encode('utf-8'),
'fedoraGroupType' : fedoraGroupType.encode('utf-8'),
'fedoraGroupNeedsSponsor' : fedoraGroupNeedsSponsor.encode('utf-8'),
'fedoraGroupUserCanRemove' : fedoraGroupUserCanRemove.encode('utf-8'),
'fedoraGroupRequires' : fedoraGroupRequires.encode('utf-8'),
'fedoraGroupJoinMsg' : fedoraGroupJoinMsg.encode('utf-8'),
}
self.__server.add('cn=%s,%s' % (cn, self.__base), attributes)
# attributes = {
# 'objectClass' : ('organizationalUnit', 'top'),
# 'ou' : 'FedoraGroups'
# }
# self.__server.add('ou=FedoraGroups,cn=%s,%s' % (cn, self.__base), attributes)
return 0
###############################################################################
# UserGroup - Determines information about a user in a group, when they joined
# who their sponsor is and their approval status are examples of
# things found in this group
###############################################################################
class UserGroup(object):
''' Individual User->Group abstraction class '''
def __init__(self, fedoraRoleApprovalDate=None, fedoraRoleSponsor=None, cn=None, fedoraRoleCreationDate=None, objectClass=None, fedoraRoleType=None, fedoraRoleStatus='Not a Member', fedoraRoleDomain=None):
self.fedoraRoleApprovalDate = fedoraRoleApprovalDate
self.fedoraRoleSponsor = fedoraRoleSponsor
self.cn = cn
self.fedoraRoleCreationDate = fedoraRoleCreationDate
self.objectClass = objectClass
self.fedoraRoleType = fedoraRoleType
self.fedoraRoleStatus = fedoraRoleStatus
self.fedoraRoleDomain = fedoraRoleDomain
###############################################################################
# Groups - Returns actual information in a group. This class actual queries
# the LDAP database.
###############################################################################
class Groups(object):
''' Class contains group information '''
__server = Server()
def __init__(self):
### FIXME: I don't think the username should be used this way.
self.__userName = None
@classmethod
def byUserName(self, cn, includeUnapproved=None, unapprovedOnly=None):
''' Return list of groups a certain user is in. Default excludes all non-approved groups'''
groups = {}
if includeUnapproved:
ldapFilter = 'objectClass=FedoraRole'
elif unapprovedOnly:
ldapFilter = '(&(!(fedoraRoleStatus=approved)) (objectClass=fedoraRole))'
else:
ldapFilter = '(&(fedoraRoleStatus=approved)(objectClass=FedoraRole))'
base = 'ou=Roles,cn=%s,ou=People,dc=fedoraproject,dc=org' % cn
try:
groupsDict = self.__server.search(base, ldapFilter)
except ldap.NO_SUCH_OBJECT:
return dict()
if not groupsDict:
groupsDict = []
for group in groupsDict:
cn = group[0][1]['cn'][0]
groups[cn] = UserGroup(
fedoraRoleApprovalDate = group[0][1]['fedoraRoleApprovalDate'][0].decode('utf8'),
fedoraRoleSponsor = group[0][1]['fedoraRoleSponsor'][0].decode('utf8'),
cn = group[0][1]['cn'][0].decode('utf8'),
fedoraRoleCreationDate = group[0][1]['fedoraRoleCreationDate'][0].decode('utf8'),
objectClass = group[0][1]['objectClass'][0].decode('utf8'),
fedoraRoleType = group[0][1]['fedoraRoleType'][0].decode('utf8'),
fedoraRoleStatus = group[0][1]['fedoraRoleStatus'][0].decode('utf8'),
fedoraRoleDomain = group[0][1]['fedoraRoleDomain'][0].decode('utf8'),
)
### FIXME: userName shouldn't be shared this way
self.__userName = cn
return groups
@classmethod
def groups(self, searchExpression='*', attributes=[]):
''' Return a list of available groups '''
groups = {}
ldapFilter = 'cn=%s' % (searchExpression)
base = 'ou=FedoraGroups,dc=fedoraproject,dc=org'
groupsDict = self.__server.search(base, ldapFilter, attributes)
if groupsDict:
for group in groupsDict:
name = group[0][1]['cn'][0].decode('utf8')
groups[name] = Group(
cn = group[0][1]['cn'][0].decode('utf8'),
fedoraGroupDesc = group[0][1]['fedoraGroupDesc'][0].decode('utf8'),
fedoraGroupOwner = group[0][1]['fedoraGroupOwner'][0].decode('utf8'),
fedoraGroupType = group[0][1]['fedoraGroupType'][0].decode('utf8'),
fedoraGroupNeedsSponsor = group[0][1]['fedoraGroupNeedsSponsor'][0].decode('utf8'),
fedoraGroupUserCanRemove = group[0][1]['fedoraGroupUserCanRemove'][0].decode('utf8'),
fedoraGroupRequires = group[0][1]['fedoraGroupRequires'][0].decode('utf8'),
fedoraGroupJoinMsg = group[0][1]['fedoraGroupJoinMsg'][0].decode('utf8'))
else:
return None
return groups
@classmethod
def remove(self, groupName, userName=None):
''' Remove user from a group '''
### FIXME: Should require the userName instead of sharing it this way
if not userName:
userName = self.__userName
try:
g = self.byUserName(userName, includeUnapproved=True)[groupName]
except:
raise TypeError, 'User not in group %s' % groupName
try:
self.__server.delete('cn=%s+fedoraRoleType=%s,ou=Roles,cn=%s,ou=People,dc=fedoraproject,dc=org' % (g.cn, g.fedoraRoleType, userName))
except ldap.NO_SUCH_OBJECT:
self.__server.delete('cn=%s,ou=Roles,cn=%s,ou=People,dc=fedoraproject,dc=org' % (g.cn, userName))
except:
raise TypeError, 'Could Not delete %s from %s' % (userName, g.cn)
@classmethod
def apply(self, groupName, userName=None):
''' Apply for a group '''
if not userName:
userName = self.__userName
if groupName in self.byUserName(userName):
# Probably shouldn't be 'TypeError'
raise TypeError, 'Already in that group'
try:
self.byGroupName(groupName)
except TypeError:
raise TypeError, 'Group "%s" does not exist' % groupName
now = time.time()
attributes = { 'cn' : groupName.encode('utf-8'),
'fedoraRoleApprovaldate' : 'None',
'fedoraRoleCreationDate' : str(now),
'fedoraRoleDomain' : 'None',
'fedoraRoleSponsor' : 'None',
'fedoraRoleStatus' : 'unapproved',
'fedoraRoleType' : 'user',
'objectClass' : ('fedoraRole')}
self.__server.add('cn=%s,ou=Roles,cn=%s,ou=People,dc=fedoraproject,dc=org' % (groupName, userName), attributes)
@classmethod
def byGroupName(cls, cn, includeUnapproved=None, unapprovedOnly=None):
''' List users in a group. Default does not show unapproved '''
self = cls()
users = {}
if includeUnapproved:
ldapFilter = 'cn=%s' % cn
elif unapprovedOnly:
ldapFilter = '(&(cn=%s) (objectClass=fedoraRole) (!(fedoraRoleStatus=approved)))' % cn
else:
ldapFilter = '(&(cn=%s) (objectClass=fedoraRole) (fedoraRoleStatus=approved))' % cn
base = 'ou=People,dc=fedoraproject,dc=org'
attributes = ['cn']
usersDict = self.__server.search(base, ldapFilter)
try:
for user in usersDict:
userName = user[0][0].split(',')[2].split('=')[1]
users[userName] = UserGroup(
fedoraRoleApprovalDate = user[0][1]['fedoraRoleApprovalDate'][0].decode('utf8'),
fedoraRoleSponsor = user[0][1]['fedoraRoleSponsor'][0].decode('utf8'),
cn = user[0][1]['cn'][0].decode('utf8'),
fedoraRoleCreationDate = user[0][1]['fedoraRoleCreationDate'][0].decode('utf8'),
objectClass = user[0][1]['objectClass'][0].decode('utf8'),
fedoraRoleType = user[0][1]['fedoraRoleType'][0].decode('utf8'),
fedoraRoleStatus = user[0][1]['fedoraRoleStatus'][0].decode('utf8'),
fedoraRoleDomain = user[0][1]['fedoraRoleDomain'][0].decode('utf8'),
)
except TypeError:
users = {}
return users
class Person(object):
'''Information and attributes about users '''
__base = 'ou=People,dc=fedoraproject,dc=org'
__server = Server()
def __init__(self):
### FIXME: Not sure what this is used for. It might be able to go
# away. It might need to be made a public attribute.
self.__filter = ''
@classmethod
def newPerson(self, cn, givenName, mail, telephoneNumber, postalAddress):
''' Create a new user '''
now = time.time()
attributes = { 'cn' : cn.encode('utf-8'),
'objectClass' : ('fedoraPerson', 'inetOrgPerson', 'organizationalPerson', 'person', 'top'),
'displayName' : cn.encode('utf-8'),
'sn' : cn.encode('utf-8'),
'cn' : cn.encode('utf-8'),
'fedoraPersonSshKey' : '',
'facsimileTelephoneNumber' : '',
'fedoraPersonApprovalStatus' : 'approved',
'givenName' : givenName.encode('utf-8'),
'mail' : mail.encode('utf-8'),
'fedoraPersonKeyId' : '',
'fedoraPersonCertSerial' : '-1',
'description' : '',
'fedoraPersonCreationDate' : str(now),
'telephoneNumber' : telephoneNumber.encode('utf-8'),
'fedoraPersonBugzillaMail' : mail.encode('utf-8'),
'postalAddress' : postalAddress.encode('utf-8'),
'fedoraPersonIrcNick' : '',
'userPassword' : 'Disabled',
'fedoraPersonTimeZone' : 'UTC',
}
self.__server.add('cn=%s,%s' % (cn, self.__base), attributes)
attributes = {
'objectClass' : ('organizationalUnit', 'top'),
'ou' : 'Roles'
}
self.__server.add('ou=Roles,cn=%s,%s' % (cn, self.__base), attributes)
return 0
### FIXME: Overriding __getattr__ and __setattr__ can be tricky and have
# performance penalties. If that's okay, you may also want to consider
# inheriting from dict as that might be a better access method.
def __getattr__(self, attr):
if attr == '__filter':
return self.__filter
if attr == 'userName':
attr = 'cn'
try:
attributes = []
attributes.append(attr)
return self.__server.search(self.__base, self.__filter, attributes)[0][0][1][attr][0].decode('utf8')
except:
# Should probably raise here.
return None
def __setattr__(self, attr, value):
if attr.startswith('_'):
#return setattr(self.__class__, attr, value)
self.__dict__[attr] = value
return
base = 'cn=%s,ou=People,dc=fedoraproject,dc=org' % self.__getattr__('cn')
if self.__getattr__(attr):
self.__server.modify(base, attr, value, self.__getattr__(attr))
else:
try:
self.__server.modify(base, attr, value)
except:
self.__server.modify(base, attr, value, self.__getattr__(attr))
@classmethod
def users(self, searchExpression='*', findAttr='cn'):
''' Returns a list of users '''
users = []
ldapFilter = '(&(objectClass=top)(%s=%s))' % (findAttr, searchExpression)
attributes = ['cn']
usersDict = self.__server.search(self.__base, ldapFilter, attributes)
if usersDict:
for user in usersDict:
users.append(user[0][1]['cn'][0].decode('utf8'))
else:
return None
return users
@classmethod
def byFilter(cls, ldapFilter):
''' Returns only the first result in the search '''
self = cls()
self.__filter = ldapFilter
return self
@classmethod
def byUserName(self, cn):
'''Wrapper for byFilter - search by cn'''
return self.byFilter('cn=%s' % cn)
@classmethod
def auth(self, who, password, ldapServer=None):
''' Basic Authentication Module '''
if not password:
raise AuthError
if not ldapServer:
s = Server()
ldapServer = s.ldapConn
who = 'cn=%s,ou=People,dc=fedoraproject,dc=org' % who
try:
ldapServer.simple_bind_s(who, password)
except:
raise AuthError
def upgrade(self, group):
''' Upgrade user in group '''
base = 'cn=%s,ou=Roles,cn=%s,ou=People,dc=fedoraproject,dc=org' % (group, self.cn)
g = Groups.byGroupName(group, includeUnapproved=True)[self.cn]
if not g.fedoraRoleStatus.lower() == 'approved':
'''User not approved or sponsored'''
raise TypeError, 'User is not approved'
if g.fedoraRoleType.lower() == 'administrator':
raise TypeError, 'User cannot be upgraded beyond administrator'
elif g.fedoraRoleType.lower() == 'sponsor':
self.__server.modify(base, 'fedoraRoleType', 'administrator', g.fedoraRoleType)
elif g.fedoraRoleType.lower() == 'user':
self.__server.modify(base, 'fedoraRoleType', 'sponsor', g.fedoraRoleType)
def downgrade(self, group):
''' Downgrade user in group '''
base = 'cn=%s,ou=Roles,cn=%s,ou=People,dc=fedoraproject,dc=org' % (group, self.cn)
g = Groups.byGroupName(group, includeUnapproved=True)[self.cn]
if not g.fedoraRoleStatus.lower() == 'approved':
'''User not approved or sponsored'''
raise TypeError, 'User is not approved'
if g.fedoraRoleType.lower() == 'user':
raise TypeError, 'User cannot be downgraded below user, did you mean remove?'
elif g.fedoraRoleType.lower() == 'sponsor':
self.__server.modify(base, 'fedoraRoleType', 'user', g.fedoraRoleType)
elif g.fedoraRoleType.lower() == 'administrator':
self.__server.modify(base, 'fedoraRoleType', 'sponsor', g.fedoraRoleType)
def sponsor(self, groupName, sponsor):
''' Sponsor current user '''
base = 'cn=%s,ou=Roles,cn=%s,ou=People,dc=fedoraproject,dc=org' % (groupName, self.cn)
g = Groups.byGroupName(groupName, includeUnapproved=True)[self.cn]
group = Groups.groups(groupName)[groupName]
now = time.time()
self.__server.modify(base, 'fedoraRoleApprovalDate', now)
if group.fedoraGroupNeedsSponsor.lower() == 'true':
self.__server.modify(base, 'fedoraRoleSponsor', sponsor)
else:
self.__server.modify(base, 'fedoraRoleSponsor', 'None')
self.__server.modify(base, 'fedoraRoleStatus', 'approved')
def generatePassword(self,password=None,length=14,salt=''):
''' Generate Password '''
secret = {} # contains both hash and password
if not password:
rand = Random()
password = ''
# Exclude 0,O and l,1
righthand = '23456qwertasdfgzxcvbQWERTASDFGZXCVB'
lefthand = '789yuiophjknmYUIPHJKLNM'
for i in range(length):
if i%2:
password = password + rand.choice(lefthand)
else:
password = password + rand.choice(righthand)
ctx = sha.new(password)
ctx.update(salt)
secret['hash'] = "{SSHA}%s" % b64encode(ctx.digest() + salt)
secret['pass'] = password
return secret
class UserAccount:
def __init__(self):
self.realName = ''
self.userName = ''
self.primaryEmail = ''
self.groups = []

View file

@ -8,8 +8,6 @@ from fas.auth import *
from fas.user import KnownUser
from textwrap import dedent
import re
class KnownGroup(validators.FancyValidator):
@ -152,7 +150,7 @@ class Group(controllers.Controller):
@expose(template="fas.templates.group.new")
def create(self, name, display_name, owner, group_type, needs_sponsor=0, user_can_remove=1, prerequisite='', joinmsg=''):
'''Create a group'''
groupname = name
person = People.by_username(turbogears.identity.current.user_name)
person_owner = People.by_username(owner)
@ -247,14 +245,16 @@ class Group(controllers.Controller):
username = turbogears.identity.current.user_name
person = People.by_username(username)
re_search = re.sub(r'\*', r'%', search).lower()
groups = Groups.query.filter(Groups.name.like(re_search)).order_by('name')
groups = filter(lambda group: canViewGroup(person, group), groups)
if len(groups) <= 0:
turbogears.flash(_("No Groups found matching '%s'") % search)
memberships = {}
for group in groups:
memberships[group.id] = group.approved_roles
groups = []
re_search = re.sub(r'\*', r'%', search).lower()
results = Groups.query.filter(Groups.name.like(re_search)).order_by('name').all()
for group in results:
if canViewGroup(person, group):
groups.append(group)
memberships[group.name] = group.approved_roles
if not len(groups):
turbogears.flash(_("No Groups found matching '%s'") % search)
return dict(groups=groups, search=search, memberships=memberships)
@identity.require(turbogears.identity.not_anonymous())
@ -284,18 +284,19 @@ class Group(controllers.Controller):
{'user': target.username, 'group': group.name})
else:
import turbomail
# TODO: How do we handle gettext calls for these kinds of emails?
# TODO: CC to right place, put a bit more thought into how to most elegantly do this
message = turbomail.Message(config.get('accounts_mail'), '%s-sponsors@fedoraproject.org' % group.name, \
"Fedora '%(group)s' sponsor needed for %(user)s" % {'user': target.username, 'group': group.name})
url = config.get('base_url_filter.base_url') + turbogears.url('/group/edit/%s' % groupname)
message.plain = dedent('''
Fedora user %(user)s, aka %(name)s <%(email)s> has requested
membership for %(applicant)s (%(applicant_name)s) in the %(group)s group and needs a sponsor.
Please go to %(url)s to take action.
''' % {'user': person.username, 'name': person.human_name, 'applicant': target.username, 'applicant_name': target.human_name, 'email': person.emails['primary'].email, 'url': url, 'group': group.name} )
message.plain = _('''
Fedora user %(user)s, aka %(name)s <%(email)s> has requested
membership for %(applicant)s (%(applicant_name)s) in the %(group)s group and needs a sponsor.
Please go to %(url)s to take action.
''') % {'user': person.username, 'name': person.human_name, 'applicant': target.username, 'applicant_name': target.human_name, 'email': person.emails['primary'].email, 'url': url, 'group': group.name}
turbomail.enqueue(message)
turbogears.flash(_('%(user)s has applied to %(group)s!') % \
{'user': target.username, 'group': group.name})
@ -326,13 +327,13 @@ class Group(controllers.Controller):
else:
import turbomail
message = turbomail.Message(config.get('accounts_mail'), target.emails['primary'].email, "Your Fedora '%s' membership has been sponsored" % group.name)
message.plain = dedent('''
%(name)s <%(email)s> has sponsored you for membership in the %(group)s
group of the Fedora account system. If applicable, this change should
propagate into the e-mail aliases and CVS repository within an hour.
message.plain = _('''
%(name)s <%(email)s> has sponsored you for membership in the %(group)s
group of the Fedora account system. If applicable, this change should
propagate into the e-mail aliases and CVS repository within an hour.
%(joinmsg)s
''') % {'group': group.name, 'name': person.human_name, 'email': person.emails['primary'].email, 'joinmsg': group.joinmsg}
%(joinmsg)s
''') % {'group': group.name, 'name': person.human_name, 'email': person.emails['primary'].email, 'joinmsg': group.joinmsg}
turbomail.enqueue(message)
turbogears.flash(_("'%s' has been sponsored!") % person.human_name)
turbogears.redirect('/group/view/%s' % group.name)
@ -364,12 +365,12 @@ class Group(controllers.Controller):
else:
import turbomail
message = turbomail.Message(config.get('accounts_mail'), target.emails['primary'].email, "Your Fedora '%s' membership has been removed" % group.name)
message.plain = dedent('''
%(name)s <%(email)s> has removed you from the '%(group)s'
group of the Fedora Accounts System This change is effective
immediately for new operations, and should propagate into the e-mail
aliases within an hour.
''') % {'group': group.name, 'name': person.human_name, 'email': person.emails['primary'].email}
message.plain = _('''
%(name)s <%(email)s> has removed you from the '%(group)s'
group of the Fedora Accounts System This change is effective
immediately for new operations, and should propagate into the e-mail
aliases within an hour.
''') % {'group': group.name, 'name': person.human_name, 'email': person.emails['primary'].email}
turbomail.enqueue(message)
turbogears.flash(_('%(name)s has been removed from %(group)s!') % \
{'name': target.username, 'group': group.name})
@ -406,12 +407,12 @@ class Group(controllers.Controller):
# Should we make person.upgrade return this?
role = PersonRoles.query.filter_by(group=group, member=target).one()
status = role.role_type
message.plain = dedent('''
%(name)s <%(email)s> has upgraded you to %(status)s status in the
'%(group)s' group of the Fedora Accounts System This change is
effective immediately for new operations, and should propagate
into the e-mail aliases within an hour.
''') % {'group': group.name, 'name': person.human_name, 'email': person.emails['primary'].email, 'status': status}
message.plain = _('''
%(name)s <%(email)s> has upgraded you to %(status)s status in the
'%(group)s' group of the Fedora Accounts System This change is
effective immediately for new operations, and should propagate
into the e-mail aliases within an hour.
''') % {'group': group.name, 'name': person.human_name, 'email': person.emails['primary'].email, 'status': status}
turbomail.enqueue(message)
turbogears.flash(_('%s has been upgraded!') % target.username)
turbogears.redirect('/group/view/%s' % group.name)
@ -443,12 +444,12 @@ class Group(controllers.Controller):
message = turbomail.Message(config.get('accounts_mail'), target.emails['primary'].email, "Your Fedora '%s' membership has been downgraded" % group.name)
role = PersonRoles.query.filter_by(group=group, member=target).one()
status = role.role_type
message.plain = dedent('''
%(name)s <%(email)s> has downgraded you to %(status)s status in the
'%(group)s' group of the Fedora Accounts System This change is
effective immediately for new operations, and should propagate
into the e-mail aliases within an hour.
''') % {'group': group.name, 'name': person.human_name, 'email': person.emails['primary'].email, 'status': status}
message.plain = _('''
%(name)s <%(email)s> has downgraded you to %(status)s status in the
'%(group)s' group of the Fedora Accounts System This change is
effective immediately for new operations, and should propagate
into the e-mail aliases within an hour.
''') % {'group': group.name, 'name': person.human_name, 'email': person.emails['primary'].email, 'status': status}
turbomail.enqueue(message)
turbogears.flash(_('%s has been downgraded!') % target.username)
turbogears.redirect('/group/view/%s' % group.name)
@ -493,22 +494,22 @@ class Group(controllers.Controller):
if isApproved(person, group):
message = turbomail.Message(person.emails['primary'].email, target, _('Come join The Fedora Project!'))
message.plain = _(dedent('''
%(name)s <%(email)s> has invited you to join the Fedora
Project! We are a community of users and developers who produce a
complete operating system from entirely free and open source software
(FOSS). %(name)s thinks that you have knowledge and skills
that make you a great fit for the Fedora community, and that you might
be interested in contributing.
message.plain = _('''
%(name)s <%(email)s> has invited you to join the Fedora
Project! We are a community of users and developers who produce a
complete operating system from entirely free and open source software
(FOSS). %(name)s thinks that you have knowledge and skills
that make you a great fit for the Fedora community, and that you might
be interested in contributing.
How could you team up with the Fedora community to use and develop your
skills? Check out http://fedoraproject.org/join-fedora for some ideas.
Our community is more than just software developers -- we also have a
place for you whether you're an artist, a web site builder, a writer, or
a people person. You'll grow and learn as you work on a team with other
very smart and talented people.
How could you team up with the Fedora community to use and develop your
skills? Check out http://fedoraproject.org/join-fedora for some ideas.
Our community is more than just software developers -- we also have a
place for you whether you're an artist, a web site builder, a writer, or
a people person. You'll grow and learn as you work on a team with other
very smart and talented people.
Fedora and FOSS are changing the world -- come be a part of it!''')) % {'name': person.human_name, 'email': person.emails['primary'].email}
Fedora and FOSS are changing the world -- come be a part of it!''') % {'name': person.human_name, 'email': person.emails['primary'].email}
turbomail.enqueue(message)
turbogears.flash(_('Message sent to: %s') % target)
turbogears.redirect('/group/view/%s' % group.name)

View file

@ -9,8 +9,13 @@ class Help(controllers.Controller):
'user_ircnick' : ['IRC Nick (Optional)', '<p>IRC Nick is used to identify yourself on irc.freenode.net. Please register your nick on irc.freenode.net first, then fill this in so people can find you online when they need to</p>'],
'user_primary_email' : ['Primary Email (Required)', '<p>This email address should be your prefered email contact and will be used to send various official emails to. This is also where your @fedoraproject.org email will get forwarded</p>'],
'user_human_name' : ['Full Name (Required)', '<p>Your Human Name or "real life" name</p>'],
'user_gpg_keyid' : ['GPG Key', '<p>Only required for users signing the CLA. It is generally used to prove that a message or email came from you or to encrypt information so that only the recipients can read it. See http://fedoraproject.org/wiki/Infrastructure/AccountSystem/CLAHowTo for more information</p>'],
'user_telephone' : ['Telephone', '<p>Only required for users signing the <a href="">CLA</a>. Sometimes during a time of emergency someone from the Fedora Project may need to contact you. For more information see our <a href="">Privacy Policy</a></p>'],
'user_gpg_keyid' : ['GPG Key', '<p>Only required for users signing the <a href="http://fedoraproject.org/wiki/Legal/Licenses/CLA">CLA</a>. It is generally used to prove that a message or email came from you or to encrypt information so that only the recipients can read it. See the <a href="http://fedoraproject.org/wiki/Infrastructure/AccountSystem/CLAHowTo">CLAHowTo</a> for more information</p>'],
'user_telephone' : ['Telephone', '<p>Only required for users signing the <a href="http://fedoraproject.org/wiki/Legal/Licenses/CLA">CLA</a>. Sometimes during a time of emergency someone from the Fedora Project may need to contact you. For more information see our <a href="http://fedoraproject.org/wiki/Legal/PrivacyPolicy">Privacy Policy</a></p>'],
'user_postal_address': ['Postal Address', '<p>Only required for users signing the <a href="http://fedoraproject.org/wiki/Legal/Licenses/CLA">CLA</a>. This should be a mailing address where you can be contacted. See our <a href="http://fedoraproject.org/wiki/Legal/PrivacyPolicy">Privacy Policy</a> about any concerns.</p>'],
'user_timezone': ['Timezone (Optional)', '<p>Please specify the time zone you are in.</p>'],
'user_comments': ['Comments (Optional)', '<p>Misc comments about yourself.</p>'],
'user_account_status': ['Account Status', '<p>Shows account status, possible values include<ul><li>Valid</li><li>Disabled</li><li>Expired</li></ul></p>'],
'user_cla' : ['CLA', '<p>In order to become a full Fedora contributor you must sign a <a href="http://fedoraproject.org/wiki/Legal/Licenses/CLA">Contributor License Agreement</a>. This license is a legal agreement between you and Red Hat. Full status allows people to contribute content and code and is recommended for anyone interested in getting involved in the Fedora Project. To find out more, see the <a href="http://fedoraproject.org/wiki/Infrastructure/AccountSystem/CLAHowTo">CLAHowTo</a>.</p>'],
}
def __init__(self):

View file

@ -22,6 +22,7 @@
Model for the Fedora Account System
'''
from datetime import datetime
import pytz
from turbogears.database import metadata, mapper, get_engine
# import some basic SQLAlchemy classes for declaring the data model
# (see http://www.sqlalchemy.org/docs/04/ormtutorial.html)
@ -80,7 +81,7 @@ UnApprovedRolesSelect = PersonRolesTable.select(and_(
visits_table = Table('visit', metadata,
Column('visit_key', String(40), primary_key=True),
Column('created', DateTime, nullable=False, default=datetime.now),
Column('created', DateTime, nullable=False, default=datetime.now(pytz.utc)),
Column('expiry', DateTime)
)
@ -173,7 +174,7 @@ class People(SABase):
role = PersonRoles.query.filter_by(member=cls, group=group).one()
role.role_status = 'approved'
role.sponsor_id = requester.id
role.approval = datetime.now()
role.approval = datetime.now(pytz.utc)
def remove(cls, group, requester):
role = PersonRoles.query.filter_by(member=cls, group=group).one()

View file

@ -11,7 +11,10 @@ from openid.store.filestore import FileOpenIDStore
from fas.auth import *
from fas.user import knownUser, userNameExists
from fas.user import KnownUser
class UserID(validators.Schema):
targetname = KnownUser
class OpenID(controllers.Controller):
@ -28,8 +31,8 @@ class OpenID(controllers.Controller):
@expose(template="fas.templates.openid.about")
def about(self):
'''Display an explanatory message about the OpenID service'''
userName = turbogears.identity.current.user_name
return dict(userName=userName)
username = turbogears.identity.current.user_name
return dict(username=username)
@expose(template="genshi-text:fas.templates.openid.auth", format="text", content_type='text/plain; charset=utf-8')
def server(self, **query):
@ -58,10 +61,10 @@ class OpenID(controllers.Controller):
else:
openid_response = None
if openid_request.mode in BROWSER_REQUEST_MODES:
userName = turbogears.identity.current.user_name;
username = turbogears.identity.current.user_name;
url = None
if userName is not None:
url = config.get('base_url') + turbogears.url('/openid/id/%s' % userName)
if username is not None:
url = config.get('base_url') + turbogears.url('/openid/id/%s' % username)
if openid_request.identity == url:
if openid_request.trust_root in session['openid_trusted']:
openid_response = openid_request.answer(True)
@ -95,16 +98,15 @@ class OpenID(controllers.Controller):
@expose()
def login(self):
'''This exists only to make the user login and then redirect to /openid/server'''
userName = turbogears.identity.current.user_name;
turbogears.redirect('/openid/server')
return dict()
@expose(template="fas.templates.openid.id")
@validate(validators=userNameExists())
def id(self, userName):
@validate(validators=UserID())
def id(self, username):
'''The "real" OpenID URL'''
user = Person.byUserName(userName)
person = Person.by_username(username)
server = config.get('base_url') + turbogears.url('/openid/server')
return dict(user=user, server=server)
return dict(person=person, server=server)

View file

@ -126,10 +126,6 @@ class SaFasIdentityProvider(SqlAlchemyIdentityProvider):
log.info(_("Loading: %(visitmod)s") % \
{'visitmod': visit_identity_class_path})
visit_identity_class = load_class(visit_identity_class_path)
# 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):
'''
@ -190,7 +186,6 @@ class SaFasIdentityProvider(SqlAlchemyIdentityProvider):
'''
return user.password == crypt.crypt(password, user.password)
return user.password == self.encrypt_password(password)
def load_identity(self, visit_key):
'''Lookup the principal represented by visit_key.

View file

@ -189,6 +189,21 @@ a
background: #082C59;
}
#language
{
padding: 1ex;
}
#language label
{
color: #FFFFFF;
}
#language input
{
width: 4ex;
}
#content
{
margin-left: 22ex;

View file

@ -10,7 +10,9 @@
<?python
from fas import auth
from fas.model import People
import pytz
person = People.by_username(tg.identity.user.username)
timezone = pytz.timezone(person.timezone)
?>
<h2>${group.display_name} (${group.name})</h2>
<h3>
@ -70,12 +72,8 @@
<td><a href="${tg.url('/user/view/%s' % role.member.username)}">${role.member.username}</a></td>
<td py:if='not(role.member.username == "None")'><a href="${tg.url('/user/view/%s' % role.member.username)}">${role.member.username}</a></td>
<td py:if='role.member.username == "None"'>${_('None')}</td>
<?python
from datetime import datetime
from pytz import timezone
?>
<td>${role.creation}</td>
<td>${role.approval}</td>
<td>${role.creation.astimezone(timezone).strftime('%Y-%m-%d %H:%M:%S %Z')}</td>
<td>${role.approval.astimezone(timezone).strftime('%Y-%m-%d %H:%M:%S %Z')}</td>
<td>${role.role_status}</td>
<td>${role.role_type}</td>
<!-- This section includes all action items -->

View file

@ -9,10 +9,10 @@
<head py:match="head" py:attrs="select('@*')">
<link href="${tg.url('/static/css/style.css')}" rel="stylesheet" type="text/css" />
<meta py:replace="select('*|text()')" />
<script type="text/javascript" src="/static/js/prototype.js"></script>
<script type="text/javascript" src="/static/js/prototype.improvements.js"></script>
<script type="text/javascript" src="/static/js/scriptaculous.js"></script>
<script type="text/javascript" language="JavaScript" src="/static/js/HelpBalloon.js"></script>
<script type="text/javascript" src="${tg.url('/static/js/prototype.js')}"></script>
<script type="text/javascript" src="${tg.url('/static/js/prototype.improvements.js')}"></script>
<script type="text/javascript" src="${tg.url('/static/js/scriptaculous.js?load=effects')}"></script>
<script type="text/javascript" src="${tg.url('/static/js/HelpBalloon.js')}"></script>
</head>
@ -64,6 +64,13 @@
<li><a href="${tg.url('/group/list/A*')}">${_('Apply For a new Group')}</a></li>
<li><a href="http://fedoraproject.org/wiki/FWN/LatestIssue">${_('News')}</a></li>
</ul>
<div py:if="tg.identity.anonymous" id="language">
<form action="${tg.url('/language')}" method="get">
<label for="locale">${_('Locale:')}</label>
<input type="text" name="locale" id="locale" />
<input type="submit" value="${_('OK')}" />
</form>
</div>
</div>
<div id="content">
<div py:if="tg_flash" class="flash">

View file

@ -7,9 +7,9 @@
<title>${_('Fedora Accounts System')}</title>
</head>
<body>
<h2>${_{'Fedora Project OpenID Provider')}</h2>
<h2>${_('Fedora Project OpenID Provider')}</h2>
<p>
${Markup_('Description goes here, &lt;a href="http://username.fedorapeople.org/"&gt;username.fedorapeople.org&lt;/a&gt;'))}
${Markup_('Description goes here, &lt;a href="http://username.fedorapeople.org/"&gt;username.fedorapeople.org&lt;/a&gt;')}
</p>
</body>
</html>

View file

@ -8,13 +8,13 @@
<link rel="openid.server" href="${server}" />
</head>
<body>
<h2>${_('User %s') % user.cn}</h2>
<h2>${_('User %s') % person.username}</h2>
<div class="userbox">
<dl>
<dt>${_('Username:')}</dt>
<dd>${user.cn}</dd>
<dd>${person.username}</dd>
<dt>${_('Name:')}</dt>
<dd>${user.givenName}</dd>
<dd>${person.human_name}</dd>
</dl>
</div>
</body>

View file

@ -13,7 +13,7 @@
<input type="hidden" id="url" name="url" value="${url}" />
<input type="checkbox" id="trusted" name="trusted" value="allow" />
<label for="trusted">${Markup(_('Allow &lt;strong&gt;%s&lt;/strong&gt; to authenticate to your OpenID identity?') % url)}</label><br />
<input type="submit" value="${_('Submit'}" />
<input type="submit" value="${_('Submit')}" />
</div>
</form>
</body>

View file

@ -41,7 +41,8 @@
</div>
<div class="field">
<label for="postal_address">${_('Postal Address')}:</label>
<textarea id="postal_address" name="postal_address">${target.postal_address}</textarea>
<textarea id="postal_address" name="postal_address">${target.postal_address}</textarea>
<script type="text/javascript">var hb6 = new HelpBalloon({dataURL: '/fas/help/get_help/user_postal_address'});</script>
</div>
<div class="field">
<label for="timezone">${_('Time Zone')}:</label>
@ -51,10 +52,22 @@
?>
<option py:for="tz in common_timezones" value="${tz}" py:attrs="{'selected': target.timezone == tz and 'selected' or None}">${tz}</option>
</select>
<script type="text/javascript">var hb7 = new HelpBalloon({dataURL: '/fas/help/get_help/user_timezone'});</script>
</div>
<div class="field">
<label for="locale">${_('Locale')}:</label>
<input type="text" id="locale" name="locale" value="${target.locale}" />
<!--
<select id="locale" name="locale">
option py:for="locale in available_locales" value="${locale}" py:attrs="{'selected': target.locale == locale and 'selected' or None}">${locale}</option>
</select>
-->
<!--<script type="text/javascript">var hb7 = new HelpBalloon({dataURL: '/fas/help/get_help/locale'});</script>-->
</div>
<div class="field">
<label for="comments ">${_('Comments')}:</label>
<textarea id="comments" name="comments">${target.comments}</textarea>
<textarea id="comments" name="comments">${target.comments}</textarea>
<script type="text/javascript">var hb8 = new HelpBalloon({dataURL: '/fas/help/get_help/user_comments'});</script>
</div>
<div class="field">
<input type="submit" value="${_('Save!')}" />

View file

@ -23,12 +23,14 @@
<dt>${_('Postal Address:')}</dt><dd>${person.postal_address}&nbsp;</dd>
<dt>${_('Comments:')}</dt><dd>${person.comments}&nbsp;</dd>
<dt>${_('Password:')}</dt><dd><span class="approved">${_('Valid')}</span> <a href="${tg.url('/user/changepass')}" py:if="personal">(change)</a></dd>
<dt>${_('Account Status:')}</dt><dd><span class="approved">${_('Valid')}</span></dd>
<dt>${_('Account Status:')}</dt><dd><span class="approved">${_('Valid')}</span>
<script type="text/javascript">var hb1 = new HelpBalloon({dataURL: '/fas/help/get_help/user_account_status'});</script></dd>
<!-- cla = {None, 'signed', 'clicked'} -->
<dt>${_('CLA:')}</dt><dd>
<span py:if="cla == 'signed'" class="approved">${_('Signed CLA')}</span>
<span py:if="cla == 'clicked'" class="approved">${_('Click-through CLA')}<py:if test="personal">(<a href="${tg.url('/cla/')}">${_('GPG Sign it!')}</a></py:if>)</span>
<span py:if="not cla" class="unapproved">${_('Not Done')}<py:if test="personal"> (<a href="${tg.url('/cla/')}">${_('Sign it!')}</a>)</py:if></span></dd>
<span py:if="not cla" class="unapproved">${_('Not Done')}<py:if test="personal"> (<a href="${tg.url('/cla/')}">${_('Sign it!')}</a>)</py:if></span>
<script type="text/javascript">var hb2 = new HelpBalloon({dataURL: '/fas/help/get_help/user_cla'});</script></dd>
</dl>
</div>
<h3 py:if="personal">${_('Your Roles')}</h3>

View file

@ -1,13 +0,0 @@
from fasLDAP import add, Groups, delete
attributes = { 'cn' : 'infrastructureTest',
'fedoraRoleApprovaldate' : 'None',
'fedoraRoleCreationDate' : 'None',
'fedoraRoleDomain' : 'None',
'fedoraRoleSponsor' : 'None',
'fedoraRoleStatus' : 'unapproved',
'fedoraRoleType' : 'user',
'objectClass' : ('fedoraRole')}
print "add('cn=infrastructureTest,ou=Roles,cn=mmcgrath,ou=People,dc=fedoraproject,dc=org', attributes)"
print "delete('cn=infrastructureTest,ou=Roles,cn=mmcgrath,ou=People,dc=fedoraproject,dc=org')"

View file

@ -17,8 +17,6 @@ from fas.model import Log
from fas.auth import *
from textwrap import dedent
from random import Random
import sha
from base64 import b64encode
@ -53,9 +51,9 @@ class UnknownUser(validators.FancyValidator):
return
except:
raise validators.Invalid(_("Error: Could not create - '%s'") % value, value, state)
raise validators.Invalid(_("'%s' already exists.") % value, value, state)
class ValidUsername(validators.FancyValidator):
'''Make sure that a username isn't blacklisted'''
def _to_python(self, value, state):
@ -75,7 +73,7 @@ class UserSave(validators.Schema):
#fedoraPersonBugzillaMail = validators.Email(strip=True, max=128)
#fedoraPersonKeyId- Save this one for later :)
postal_address = validators.String(max=512)
class UserCreate(validators.Schema):
username = validators.All(
UnknownUser,
@ -103,7 +101,7 @@ class UserView(validators.Schema):
class UserEdit(validators.Schema):
targetname = KnownUser
def generate_password(password=None, length=14):
''' Generate Password '''
secret = {} # contains both hash and password
@ -114,7 +112,7 @@ def generate_password(password=None, length=14):
password = ''
for i in xrange(length):
password += random.choice(chars)
secret['hash'] = crypt.crypt(password, "$1$%s" % generate_salt(8))
secret['pass'] = password
@ -206,7 +204,7 @@ class User(controllers.Controller):
@validate(validators=UserSave())
@error_handler(error)
@expose(template='fas.templates.user.edit')
def save(self, targetname, human_name, telephone, postal_address, email, ircnick=None, gpg_keyid=None, comments='', timezone='UTC'):
def save(self, targetname, human_name, telephone, postal_address, email, ircnick=None, gpg_keyid=None, comments='', locale='en', timezone='UTC'):
username = turbogears.identity.current.user_name
target = targetname
person = People.by_username(username)
@ -225,6 +223,7 @@ class User(controllers.Controller):
target.telephone = telephone
target.postal_address = postal_address
target.comments = comments
target.locale = locale
target.timezone = timezone
except TypeError:
turbogears.flash(_('Your account details could not be saved: %s' % e))
@ -245,7 +244,7 @@ class User(controllers.Controller):
if people.count() < 0:
turbogears.flash(_("No users found matching '%s'") % search)
return dict(people=people, search=search)
@expose(template='fas.templates.user.new')
def new(self):
if turbogears.identity.not_anonymous():
@ -257,7 +256,7 @@ class User(controllers.Controller):
@error_handler(error)
@expose(template='fas.templates.new')
def create(self, username, human_name, email, telephone, postal_address):
# TODO: Ensure that e-mails are unique- this should probably be done in the LDAP schema.
# TODO: Ensure that e-mails are unique?
# Also, perhaps implement a timeout- delete account
# if the e-mail is not verified (i.e. the person changes
# their password) withing X days.
@ -271,45 +270,44 @@ class User(controllers.Controller):
person.emails['primary'] = PersonEmails(email=email, purpose='primary')
newpass = generate_password()
message = turbomail.Message(config.get('accounts_mail'), person.emails['primary'].email, _('Welcome to the Fedora Project!'))
message.plain = _(dedent('''
You have created a new Fedora account!
Your new password is: %s
message.plain = _('''
You have created a new Fedora account!
Your new password is: %s
Please go to https://admin.fedoraproject.org/fas/ to change it.
Please go to https://admin.fedoraproject.org/fas/ to change it.
Welcome to the Fedora Project. Now that you've signed up for an
account you're probably desperate to start contributing, and with that
in mind we hope this e-mail might guide you in the right direction to
make this process as easy as possible.
Fedora is an exciting project with lots going on, and you can
contribute in a huge number of ways, using all sorts of different
skill sets. To find out about the different ways you can contribute to
Fedora, you can visit our join page which provides more information
about all the different roles we have available.
http://fedoraproject.org/en/join-fedora
If you already know how you want to contribute to Fedora, and have
found the group already working in the area you're interested in, then
there are a few more steps for you to get going.
Foremost amongst these is to sign up for the team or project's mailing
list that you're interested in - and if you're interested in more than
one group's work, feel free to sign up for as many mailing lists as
you like! This is because mailing lists are where the majority of work
gets organised and tasks assigned, so to stay in the loop be sure to
keep up with the messages.
Once this is done, it's probably wise to send a short introduction to
the list letting them know what experience you have and how you'd like
to help. From here, existing members of the team will help you to find
your feet as a Fedora contributor.
And finally, from all of us here at the Fedora Project, we're looking
forward to working with you!
Welcome to the Fedora Project. Now that you've signed up for an
account you're probably desperate to start contributing, and with that
in mind we hope this e-mail might guide you in the right direction to
make this process as easy as possible.
''') % newpass['pass'])
Fedora is an exciting project with lots going on, and you can
contribute in a huge number of ways, using all sorts of different
skill sets. To find out about the different ways you can contribute to
Fedora, you can visit our join page which provides more information
about all the different roles we have available.
http://fedoraproject.org/en/join-fedora
If you already know how you want to contribute to Fedora, and have
found the group already working in the area you're interested in, then
there are a few more steps for you to get going.
Foremost amongst these is to sign up for the team or project's mailing
list that you're interested in - and if you're interested in more than
one group's work, feel free to sign up for as many mailing lists as
you like! This is because mailing lists are where the majority of work
gets organised and tasks assigned, so to stay in the loop be sure to
keep up with the messages.
Once this is done, it's probably wise to send a short introduction to
the list letting them know what experience you have and how you'd like
to help. From here, existing members of the team will help you to find
your feet as a Fedora contributor.
And finally, from all of us here at the Fedora Project, we're looking
forward to working with you!
''') % newpass['pass']
turbomail.enqueue(message)
person.password = newpass['pass']
turbogears.flash(_('Your password has been emailed to you. Please log in with it and change your password'))
@ -352,7 +350,7 @@ class User(controllers.Controller):
turbogears.flash(_('You are already logged in!'))
turbogears.redirect('/user/view/%s' % turbogears.identity.current.user_name)
return dict()
@expose(template="fas.templates.user.resetpass")
def sendpass(self, username, email, encrypted=False):
import turbomail
@ -368,13 +366,17 @@ class User(controllers.Controller):
return dict()
newpass = generate_password()
message = turbomail.Message(config.get('accounts_mail'), email, _('Fedora Project Password Reset'))
mail = _(dedent('''
You have requested a password reset!
Your new password is: %s
Please go to https://admin.fedoraproject.org/fas/ to change it.
''')) % newpass['pass']
mail = _('''
You have requested a password reset!
Your new password is: %s
Please go to https://admin.fedoraproject.org/fas/ to change it.
''') % newpass['pass']
if encrypted:
# TODO: Move this out to a single function (same as
# CLA one), think of how to make sure this doesn't get
# full of random keys (keep a clean Fedora keyring)
# TODO: MIME stuff?
try:
subprocess.check_call([config.get('gpgexec'), '--keyserver', config.get('gpg_keyserver'), '--recv-keys', person.gpg_keyid])
except subprocess.CalledProcessError:

View file

@ -51,13 +51,13 @@ CREATE TABLE people (
affiliation TEXT,
certificate_serial INTEGER DEFAULT 1,
-- tg_user::created
creation TIMESTAMP DEFAULT NOW(),
creation TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
--approval_status TEXT DEFAULT 'unapproved',
internal_comments TEXT,
ircnick TEXT,
last_seen TIMESTAMP DEFAULT NOW(),
last_seen TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
status TEXT,
status_change TIMESTAMP DEFAULT NOW(),
status_change TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
locale TEXT not null DEFAULT 'C',
timezone TEXT null DEFAULT 'UTC',
latitude numeric,
@ -123,7 +123,7 @@ CREATE TABLE groups (
prerequisite_id INTEGER REFERENCES groups(id),
joinmsg TEXT NULL DEFAULT '',
-- tg_group::created
creation TIMESTAMP DEFAULT NOW(),
creation TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
check (group_type in ('bugzilla','cvs', 'bzr', 'git', 'hg', 'mtn',
'svn', 'shell', 'torrent', 'tracker', 'tracking', 'user'))
);
@ -159,8 +159,8 @@ CREATE TABLE person_roles (
role_status text DEFAULT 'unapproved',
internal_comments text,
sponsor_id INTEGER REFERENCES people(id),
creation TIMESTAMP DEFAULT NOW(),
approval TIMESTAMP DEFAULT NULL,
creation TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
approval TIMESTAMP WITH TIME ZONE DEFAULT NULL,
primary key (person_id, group_id),
check (role_status in ('approved', 'unapproved')),
check (role_type in ('user', 'administrator', 'sponsor'))
@ -180,8 +180,8 @@ CREATE TABLE group_roles (
role_status text DEFAULT 'unapproved',
internal_comments text,
sponsor_id INTEGER REFERENCES people(id),
creation TIMESTAMP DEFAULT NOW(),
approval TIMESTAMP DEFAULT NOW(),
creation TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
approval TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
primary key (member_id, group_id),
check (role_status in ('approved', 'unapproved')),
check (role_type in ('user', 'administrator', 'sponsor'))
@ -209,7 +209,7 @@ create table bugzilla_queue (
create table log (
id serial primary key,
author_id INTEGER references people(id) not null,
changetime TIMESTAMP default NOW(),
changetime TIMESTAMP WITH TIME ZONE default NOW(),
description TEXT
);
@ -233,7 +233,7 @@ create table requests (
hostname TEXT not null,
ip TEXT not null,
action TEXT not null default 'trust_all',
last_request TIMESTAMP default now() not null,
last_request TIMESTAMP WITH TIME ZONE default now() not null,
approved boolean,
unique (person_id, hostname, ip, action)
);
@ -249,8 +249,8 @@ cluster requests_last_request_idx on requests;
--
create table visit (
visit_key CHAR(40) primary key,
created TIMESTAMP not null default now(),
expiry TIMESTAMP
created TIMESTAMP WITH TIME ZONE not null default now(),
expiry TIMESTAMP WITH TIME ZONE
);
create index visit_expiry_idx on visit(expiry);

View file

@ -1,22 +0,0 @@
#this is the fedoraPerson shema file for use with the Account system
#it is based on the inetOrgPerson shema, but has some spicific
#attribues added onto it for use by the Account system
#for now this is the contents
#sshkey bugzillaemail ircNick approvalStatus creationDate
dn: cn=schema
attributeTypes: ( 2.5.444.8 NAME 'fedoraPersonSshKey' DESC 'ssh key for this member' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{4096} SINGLE-VALUE)
attributeTypes: ( 2.5.444.9 NAME 'fedoraPersonBugzillaMail' DESC 'members preferred bugzilla email address' EQUALITY caseIgnoreIA5Match SUBSTR caseIgnoreIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} )
attributeTypes: ( 2.5.444.16 NAME 'fedoraPersonIrcNick' DESC 'irc nick of the user on freenode' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{100} )
attributetypes: ( 2.5.444.17 NAME 'fedoraPersonCreationDate' DESC 'date entry was created' EQUALITY caseIgnoreIA5Match SUBSTR caseIgnoreIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
attributeTypes: ( 2.5.444.18 NAME 'fedoraPersonApprovalStatus' DESC 'users approval status' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{50} )
attributeTypes: ( 2.5.444.19 NAME 'fedoraPersonKeyId' DESC 'users GPG key ID' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{50} )
attributeTypes: ( 2.5.444.22 NAME 'fedoraPersonCertSerial' DESC 'users SSL cert serial' EQUALITY IntegerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
attributeTypes: ( 2.5.444.23 NAME 'fedoraPersonTimeZone' DESC 'time zone of the user' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{50} )
attributeTypes: ( 2.5.444.24 NAME 'fedoraPersonEmailConfirm' DESC 'user email confirmation' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{50} )
attributeTypes: ( 2.5.444.25 NAME 'fedoraPersonEmailCode' DESC 'user email code' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{50} )
# fedoraPerson
# The fedoraPerson represents people who are a member of the fedora project
# in some way. It is a structural class and inherits
# from the inetOrgPerson class
objectClasses: ( 2.5.555.1 NAME 'fedoraPerson' DESC 'A member of the fedoraproject group' SUP inetOrgPerson STRUCTURAL MUST ( fedoraPersonSshKey $ mail $ fedoraPersonCreationDate $ fedoraPersonTimeZone ) MAY (fedoraPersonIrcNick $ fedoraPersonApprovalStatus $ fedoraPersonBugzillaMail $ fedoraPersonKeyId $ fedoraPersonCertSerial ) )

View file

@ -1,28 +0,0 @@
#this is the fedoraMembership shema file for use with the Account system
#check out http://www.openldap.org/doc/admin23/schema.html#Extending%20Schema
#for more information
#for now this is the contents
#role_type
#role_domain
#role_status
#internal_comments
#sponsor (points to the sponsor's DN)
#creation (date)
#approval (date)
#don't have a name use cn instead
#attributeType ( 2.5.444.1 NAME 'name'
# DESC 'group name'
# EQUALITY caseIgnoreMatch
# SUBSTR caseIgnoreSubstringsMatch
# SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{50} )
dn: cn=schema
attributeTypes: ( 2.5.444.2 NAME 'fedoraRoleType' DESC 'the type of role' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{25} )
attributeTypes: ( 2.5.444.15 NAME 'fedoraRoleDomain' DESC 'the domain of this role' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{15} )
attributeTypes: ( 2.5.444.3 NAME 'fedoraRoleStatus' DESC 'the approval status of this role' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{15} )
#attributeTypes: ( 2.5.444.4 NAME 'internalComments'
# DESC 'group membership comments'
# SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{1000} )
attributeTypes: ( 2.5.444.5 NAME 'fedoraRoleSponsor' DESC 'role sponsor' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{25} )
attributeTypes: ( 2.5.444.6 NAME 'fedoraRoleCreationDate' DESC 'membership creation date' EQUALITY caseIgnoreIA5Match SUBSTR caseIgnoreIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
attributeTypes: ( 2.5.444.7 NAME 'fedoraRoleApprovalDate' DESC 'membership approval date' EQUALITY caseIgnoreIA5Match SUBSTR caseIgnoreIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
objectClasses: ( 2.5.555.2 NAME 'fedoraRole' DESC 'An object describing a persons roles with the fedora project' STRUCTURAL MUST ( cn ) MAY ( fedoraRoleApprovalDate $ fedoraRoleCreationDate $ fedoraRoleType $ fedoraRoleStatus $ fedoraRoleDomain $ fedoraRoleSponsor ) )

View file

@ -1,17 +0,0 @@
#this is the fedora group schema file for use with the accounts system2
#it currently contains the following:
#owner (the owner's DN)
#group_type
#needs_sponsor
#user_can_remove (will be handled by ACLs, but we need to clue the interface about it)
#prerequisite_id (no idea what that is, so maybe not) -- not included yet
#joinmsg
dn: cn=schema
attributeTypes: ( 2.5.444.10 NAME 'fedoraGroupOwner' DESC 'group owner' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{50} )
attributeTypes: ( 2.5.444.11 NAME 'fedoraGroupType' DESC 'the type of group' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{50} )
attributeTypes: ( 2.5.444.12 NAME 'fedoraGroupNeedsSponsor' DESC 'boolean indicating whether or not the group needs a sponsor' EQUALITY booleanMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 )
attributeTypes: ( 2.5.444.13 NAME 'fedoraGroupUserCanRemove' DESC 'boolean indicating whether or not the user can remove the group' EQUALITY booleanMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 )
attributeTypes: ( 2.5.444.14 NAME 'fedoraGroupJoinMsg' DESC 'message received upon joining the group' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{1000} )
attributeTypes: ( 2.5.444.21 NAME 'fedoraGroupDesc' DESC 'group description' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{100} )
attributeTypes: ( 2.5.444.20 NAME 'fedoraGroupRequires' DESC 'Requisites of this Group' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{50} )
objectClasses: ( 2.5.555.3 NAME 'fedoraGroup' DESC 'A object describing a group entry' STRUCTURAL MUST ( cn $ fedoraGroupDesc $ fedoraGroupOwner ) MAY ( fedoraGroupJoinMsg $ fedoraGroupUsercanRemove $ fedoraGroupType $ fedoraGroupNeedsSponsor $ fedoraGroupRequires ) )

View file

@ -1,434 +0,0 @@
#!/usr/bin/env python
"""
PgToLDAP is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
PgToLDAP 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with PgToLDAP; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Id: $Id: PgToLDAP.py,v 1.5 2006/12/07 01:40:06 lyz Exp $
"""
import sys, time
import ldap, ldif, pgdb, ldap.modlist
from optparse import OptionParser
import sha
import base64
from random import randrange
version = "0.112"
def encode_SSHA_password ( password ):
p_ssha = sha.new( password )
salt = ''
for n in range(7):
salt += chr(randrange(256))
p_ssha.update( salt )
p_ssha_base64 = base64.encodestring(p_ssha.digest() + salt + '' )
return '%s%s' %( '{SSHA}', p_ssha_base64 )
def parseArgs():
parser = OptionParser(version="%prog " + version)
parser.add_option ("-v", "--verbose", dest="verbose", action="store_true", default=False,
help="Verbose output")
parser.add_option ("--pgUser", dest="pgUser", default="postgres",
help="PostgreSQL User [default: %default]", metavar="USER")
parser.add_option ("--pgPassword", dest="pgPassword",
help="PostgreSQL Password", metavar="PASSWORD")
parser.add_option ("--pgHost", dest="pgHost", default="localhost",
help="PostgreSQL Host [default: %default]", metavar="HOST")
parser.add_option ("--pgPort", dest="pgPort", default="5432",
help="PostgreSQL Port [default: %default]", metavar="PORT")
parser.add_option ("--pgDb", dest="pgDB",
help="PostgreSQL Database", metavar="DATABASE")
parser.add_option ("-o", "--output", dest="outType", default="file",
help="Output Type [file|ldap] [default: %default]")
parser.add_option ("-f", "--file", dest="outFile", default="out.ldif",
help="Output file [default: %default]", metavar="FILE")
parser.add_option ("--ldapUser", dest="ldapUser", default="cn=Directory Manager",
help="LDAP User [default: %default]", metavar="USER")
parser.add_option ("--ldapPassword", dest="ldapPassword",
help="LDAP Password", metavar="PASSWORD")
parser.add_option ("--ldapHost", dest="ldapHost", default="localhost",
help="LDAP Host [default: %default]", metavar="HOST")
parser.add_option ("--ldapPort", dest="ldapPort", default="389",
help="LDAP Port [default: %default]", metavar="PORT")
parser.add_option ("--ldapOU", dest="ldapBaseOU", default="dc=fedoraproject, dc=org",
help="LDAP Base OU [default: %default]", )
(options, args) = parser.parse_args()
if options.outType != "file" and options.outType != "ldap":
parser.error("Output type must be file or ldap")
return (options, args)
def connPostgres(user, password, db, host, port):
"""Tries to connect to the Postgres db server.
Will exit with exit code 1 it it fails."""
global verbose
if verbose:
print "Connecting to postgres://%s@%s:%s" % (user, host, port)
try:
dbConn = pgdb.connect(user=user,
password=password,
database=db,
host='%s:%s' %(host, port))
return dbConn
except:
print "Error connecting to Postgres server"
# TODO: Remove exit comment
sys.exit(1)
def connLDAP(user, password, host, port):
"""Tries to bind to the LDAP server.
Will exit with exit code 1 it it fails."""
global verbose
if verbose:
print "Connecting to ldap://%s@%s:%s" % (user, host, port)
try:
ldapConn = ldap.open(host)
ldapConn.protocol_version = ldap.VERSION3
ldapConn.simple_bind_s(user, password)
return ldapConn
except ldap.LDAPError, error_message:
print 'Error connecting to LDAP Server'
print error_message
sys.exit(1)
def openLdifFile(filename):
"""Tries to open the output file for writing.
Will exit with exit code 1 it it fails."""
global verbose
if verbose:
print "Opening output file %s" % filename
try:
#ldifWriter = ldif.LDIFWriter(ldap.initialize('ldap://localhost:1390'),filename)
fileHandel = open (filename,'w')
# | __init__(self, output_file, base64_attrs=None, cols=76, line_sep='\n')
# | output_file
# | file object for output
# | base64_attrs
# | list of attribute types to be base64-encoded in any case
# | cols
# | Specifies how many columns a line may have before it's
# | folded into many lines.
# | line_sep
# | String used as line separator
ldifWriter = ldif.LDIFWriter(fileHandel,"None")
return ldifWriter
except ldap.LDAPError, error_message:
print "Error opening output file: %s" % (filename)
print error_message
sys.exit(1)
def cleanLDAP(ldapConn, ldapBaseOU):
"""Removes all existing entries under ou=People and ou=Groups for
the defined base OU.
Will exit with exit code 1 if an LDAP error is encountered."""
global verbose
if verbose:
print "Deleting existing users from LDAP"
try:
timeout = 0
result_id = ldapConn.search("ou=People, " + ldapBaseOU,
ldap.SCOPE_ONELEVEL,
"cn=*",
None)
while 1:
result_type, result_data = ldapConn.result(result_id, timeout)
if (result_data == []):
break
else:
if result_type == ldap.RES_SEARCH_ENTRY:
if verbose:
print "Deleting LDAP user: " + result_data[0][1]['cn'][0]
ldapConn.delete_s(result_data[0][0])
except ldap.LDAPError, error_message:
print "Error deleting existing users from LDAP"
print error_message
sys.exit(1)
if verbose:
print "Deleting existing groups from LDAP"
try:
timeout = 0
result_id = ldapConn.search("ou=Groups, " + ldapBaseOU,
ldap.SCOPE_ONELEVEL,
"cn=*",
None)
while 1:
result_type, result_data = ldapConn.result(result_id, timeout)
if (result_data == []):
break
else:
if result_type == ldap.RES_SEARCH_ENTRY:
if verbose:
print "Deleting LDAP group: " + result_data[0][1]['cn'][0]
ldapConn.delete_s(result_data[0][0])
except ldap.LDAPError, error_message:
print "Error deleting existing groups from LDAP"
print error_message
sys.exit(1)
def main():
global verbose
(options, cruft) = parseArgs()
verbose = options.verbose
dbConn = connPostgres(options.pgUser, options.pgPassword,
options.pgDB, options.pgHost, options.pgPort)
# Cleanup LDAP (if necessary)
if options.outType == "ldap":
ldapConn = connLDAP(options.ldapUser, options.ldapPassword,
options.ldapHost, options.ldapPort)
#cleanLDAP(ldapConn, options.ldapBaseOU)
else:
ldifWriter = openLdifFile(options.outFile)
# Copy all users from db to ldap/ldif
# this will to queries and mappings
try:
if verbose:
print "Selecting all users from Postgres Database"
userCursor = dbConn.cursor()
userCursor.execute ("SELECT * FROM person")
#id, username, email 2, human_name 3, gpg_keyid 4, ssh_key 5, password 6, comments 7, postal_address 8, telephone 9, facsimile 10, affiliation 11, creation 12, approval_status 13, internal_comments 14, wiki_prefs 15, ircnick 16
except:
print "Error selecting users from db"
raise
sys.exit(1)
while 1:
user = userCursor.fetchone()
if user == None:
break
date = str(user[12]).split('.')[0]
timestamp = time.strftime('%s', time.strptime(date, "%Y-%m-%d %H:%M:%S"))
# TODO: Create method createLdapUserEntry(user)
#(dn, entry) = createLdapUserEntry(user)
if options.outType == "ldif":
ldifWriter.unparse(dn, entry)
else:
print "Adding ldif info for " + user[3] + "."
#userLdif = [["objectClass",["fedoraPerson","organizationalUnit"]] , [ "displayName",[ user[1] ] ] ]
userLdif = [["objectClass",["fedoraPerson"]] , [ "displayName",[ user[1] ] ] ]
userLdif.append(["mail",[str(user[2])]])
userLdif.append(["sn",[str(user[1])]])
userLdif.append(["fedoraPersonBugzillaMail",[str(user[2])]])
userLdif.append(["cn",[str(user[1])]])
userLdif.append(["givenName",[str(user[3])]])
userLdif.append(["fedoraPersonKeyId",[str(user[4])]])
userLdif.append(["fedoraPersonCertSerial",['-1']])
userLdif.append(["fedoraPersonSshKey",[str(user[5])]])
userLdif.append(["userPassword",[encode_SSHA_password(str(user[6]))]])
userLdif.append(["postalAddress",[str(user[8])]])
userLdif.append(["telephoneNumber",[str(user[9])]])
userLdif.append(["fax",[str(user[10]) or "None"]])
userLdif.append(["o",[str(user[11]) or "None" ]]) # affiliation is set to the o -- another stretch ??
userLdif.append(["fedoraPersonCreationDate",[str(timestamp)]])
userLdif.append(["fedoraPersonApprovalStatus",[str(user[13])]])
userLdif.append(["description",[str(user[14])]]) #this one may be a streach -- original field was internal comments
userLdif.append(["fedoraPersonTimeZone",["UTC"]])
userLdif.append(["fedoraPersonIrcNick",[str(user[16])]])
#userLdif.append(["ou",["Roles"]]) Adding an OU instead
print userLdif
#for userKey in userLdif.keys():
#print "Key Name -> " + userKey
#print ":::Key Value::: "
#print userLdif[userKey]
#ldifWriter.unparse("dc=fedoraproject,dc=org cn=" + user[3] , { userKey : [str(userLdif[userKey])] } )
#print userLdif.keys()
#print userLdif.values()
ldifWriter.unparse("cn=" + str(user[1]) +",ou=People,dc=fedoraproject,dc=org" , userLdif )
roleOuLdif = [["objectClass",["organizationalUnit"]] , [ "ou",[ "Roles" ] ] ]
ldifWriter.unparse("ou=Roles,cn=" + str(user[1]) +",ou=People,dc=fedoraproject,dc=org" , roleOuLdif )
#ldifWriter.unparse("dc=fedoraproject,dc=org, cn=" + user[3] , [ ['ano',['domini']],['uances',['od']] ])
#time.sleep (2)
#ldapConn.add_s(dn, entry)
userCursor.close()
# Select all groups from the DB
try:
if verbose:
print "Selecting all groups from Postgres Database"
groupCursor = dbConn.cursor()
groupCursor.execute ("SELECT * FROM project_group")
except:
print "Error selecting groups from db"
raise
sys.exit(1)
while 1:
group = groupCursor.fetchone()
if group == None:
break
# TODO: Create method createLdapGroupEntry(group)
#(dn, entry) = createLdapGroupEntry(group)
if options.outType == "ldif":
ldifWriter.unparse(dn, entry)
else:
#ldapConn.add_s(dn, entry)
print "Adding group info for %s." % group[7]
#id0, owner_id1, group_type2, needs_sponsor3, user_can_remove4, prerequisite_id5, joinmsg6, name7
uidLookupCursor = dbConn.cursor()
uidLookupCursor.execute ("SELECT username FROM person where id =" + str(group[1]) )
owner = uidLookupCursor.fetchone()
if str(group[5]) != "None" :
uidLookupCursor.execute ("SELECT name FROM project_group where id =" + str(group[5]) )
prereq = uidLookupCursor.fetchone()
print prereq
else:
prereq=["None"]
print owner
#id0, name1, owner_id2, group_type3, needs_sponsor4, user_can_remove5, prerequisite_id6, joinmsg7
userLdif = [["objectClass",["fedoraGroup"]] ]
userLdif.append(["cn",[str(group[7])]])
userLdif.append(["fedoraGroupOwner",owner]) # need to get a cn for this not just the id
#userLdif.append(["groupOwner",[str(group[2])]]) # need to get a cn for this not just the id
userLdif.append(["fedoraGroupType",[str(group[3]) or "None" ]])
#we're using the boolean type for these. This means they need to be converted to the TRUE and FALSE strings
if str(group[3]) == "0" :
group[3]="FALSE"
else:
group[3]="TRUE"
if str(group[4]) == "0" :
group[4]="FALSE"
else:
group[4]="TRUE"
if group[5] == None:
group[5] = ""
userLdif.append(["fedoraGroupNeedsSponsor",[str(group[3])]]) #need to convert to bool
userLdif.append(["fedoraGroupUserCanRemove",[str(group[4])]]) #need to convert to bool
userLdif.append(["fedoraGroupDesc",[str('Please fill out a Group Description')]]) #need to convert to bool
#userLdif.append(["groupPrerequisite",[str(group[5])]])
userLdif.append(["fedoraGroupRequires",[str(group[5])]]) # <- Hope this is added properly - Ricky
#userLdif.append(["groupPrerequisite",prereq]) not currently in the schema
userLdif.append(["fedoraGroupJoinMsg",[str(group[6]) or "None" ]])
ldifWriter.unparse("cn=" + str(group[7]) +",ou=FedoraGroups,dc=fedoraproject,dc=org" , userLdif )
groupCursor.close()
# Select all roles from the DB
try:
if verbose:
print "Selecting all roles from Postgres Database"
roleCursor = dbConn.cursor()
roleCursor.execute ("SELECT * FROM role")
#person_id, project_group_id, role_type, role_domain, role_status, internal_comments, sponsor_id (Points to a person), creation (TIMESTAMP), approval (TIMESTAMP)
except:
print "Error selecting roles from db"
raise
sys.exit(1)
while 1:
role = roleCursor.fetchone()
if role == None:
break
date1 = str(role[7]).split('.')[0]
date2 = str(role[8]).split('.')[0]
try:
timestamp1 = time.strftime('%s', time.strptime(date1, "%Y-%m-%d %H:%M:%S"))
except:
timestamp1 = "None"
try:
timestamp2 = time.strftime('%s', time.strptime(date2, "%Y-%m-%d %H:%M:%S"))
except:
timestamp2 = "None"
# TODO: Create method createLdapRoleEntry(group)
#(dn, entry) = createLdapGroupRole(group)
if options.outType == "ldif":
ldifWriter.unparse(dn, entry)
else:
#ldapConn.add_s(dn, entry)
#person_id0, group_project_id1, role_type2, role_domain3, role_status4, internal_comments5, sponsor_id6, creation7, approval8
uidRoleCursor = dbConn.cursor()
uidRoleCursor.execute ("SELECT username FROM person where id =" + str(role[0]) )
username = uidRoleCursor.fetchone()
uidRoleCursor.execute ("SELECT name FROM project_group where id =" + str(role[1]) )
group = uidRoleCursor.fetchone()
if str(role[6]) != "None" :
uidRoleCursor.execute ("SELECT username FROM person where id =" + str(role[6]) )
sponsor = uidRoleCursor.fetchone()
else:
sponsor = ["None"]
print "Adding " + str(role[4]) + " role info for " + group[0] + " for user " + username[0] + "."
#if str(group[6]) != "None" :
# uidLookupCursor.execute ("SELECT name FROM project_group where id =" + str(group[6]) )
# prereq = uidLookupCursor.fetchone()
# print prereq
#else:
# prereq=["None"]
#print owner
#person_id0, group_project_id1, role_type2, role_domain3, role_status4, internal_comments5, sponsor_id6, creation7, approval8
roleLdif = [["objectClass",["fedoraRole"]] ]
#roleLdif.append(["cn",[str(group[0]) + str(role[2])]]) #Fix me
roleLdif.append(["cn",[str(group[0])]]) #Fix me
roleLdif.append(["fedoraRoleType",[str(role[2])]])
roleLdif.append(["fedoraRoleDomain",[str(role[3]) or "None" ]])
roleLdif.append(["fedoraRoleStatus",[str(role[4])]])
roleLdif.append(["fedoraRoleSponsor",sponsor])
roleLdif.append(["fedoraRoleCreationDate",[str(timestamp1)]])
roleLdif.append(["fedoraRoleApprovalDate",[str(timestamp2)]])
ldifWriter.unparse("cn=" + group[0] + ",ou=Roles,cn=" + username[0] + ",ou=People,dc=fedoraproject,dc=org" , roleLdif )
roleCursor.close()
sys.exit(1)
if __name__ == "__main__":
main()

View file

@ -1,45 +0,0 @@
#this is the fedora group schema file for use with the accounts system2
#it currently contains the following:
#owner (the owner's DN)
#group_type
#needs_sponsor
#user_can_remove (will be handled by ACLs, but we need to clue the interface about it)
#prerequisite_id (no idea what that is, so maybe not) -- not included yet
#joinmsg
attributeType ( 2.5.444.10 NAME 'fedoraGroupOwner'
DESC 'group owner'
EQUALITY caseIgnoreMatch
SUBSTR caseIgnoreSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{50} )
attributeType ( 2.5.444.11 NAME 'fedoraGroupType'
DESC 'the type of group'
EQUALITY caseIgnoreMatch
SUBSTR caseIgnoreSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{50} )
attributeType ( 2.5.444.12 NAME 'fedoraGroupNeedsSponsor'
DESC 'boolean indicating whether or not the group needs a sponsor'
EQUALITY booleanMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 )
attributeType ( 2.5.444.13 NAME 'fedoraGroupUserCanRemove'
DESC 'boolean indicating whether or not the user can remove the group'
EQUALITY booleanMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 )
attributeType ( 2.5.444.14 NAME 'fedoraGroupJoinMsg'
DESC 'message received upon joining the group'
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{1000} )
objectclass ( 2.5.555.3
NAME 'fedoraGroup'
DESC 'A object describing a group entry'
STRUCTURAL
MAY ( fedoraGroupJoinMsg $ fedoraGroupUsercanRemove $ fedoraGroupType $ fedoraGroupNeedsSponsor $ fedoraGroupUserCanRemove )
MUST ( cn $ fedoraGroupOwner )
)

View file

@ -1,57 +0,0 @@
#this is the fedoraPerson shema file for use with the Account system
#it is based on the inetOrgPerson shema, but has some spicific
#attribues added onto it for use by the Account system
#for now this is the contents
#sshkey bugzillaemail ircNick approvalStatus creationDate
attributeType ( 2.5.444.8 NAME 'fedoraPersonSshKey'
DESC 'ssh key for this member'
EQUALITY caseIgnoreMatch
SUBSTR caseIgnoreSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{4096}
SINGLE-VALUE)
attributeType ( 2.5.444.9 NAME 'fedoraPersonBugzillaMail'
DESC 'members preferred bugzilla email address'
EQUALITY caseIgnoreIA5Match
SUBSTR caseIgnoreIA5SubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} )
attributeType ( 2.5.444.16 NAME 'fedoraPersonIrcNick'
DESC 'irc nick of the user on freenode'
EQUALITY caseIgnoreMatch
SUBSTR caseIgnoreSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{100} )
attributetype ( 2.5.444.17 NAME 'fedoraPersonCreationDate'
DESC 'date entry was created'
EQUALITY caseIgnoreIA5Match
SUBSTR caseIgnoreIA5SubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
SINGLE-VALUE )
attributeType ( 2.5.444.18 NAME 'fedoraPersonApprovalStatus'
DESC 'users approval status'
EQUALITY caseIgnoreMatch
SUBSTR caseIgnoreSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{50} )
attributeType ( 2.5.444.19 NAME 'fedoraPersonKeyId'
DESC 'users GPG key ID'
EQUALITY caseIgnoreMatch
SUBSTR caseIgnoreSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{50} )
# fedoraPerson
# The fedoraPerson represents people who are a member of the fedora project
# in some way. It is a structural class and inherits
# from the inetOrgPerson class
objectclass ( 2.5.555.1
NAME 'fedoraPerson'
DESC 'A member of the fedoraproject group'
SUP inetOrgPerson
STRUCTURAL
MUST ( fedoraPersonSshKey $ mail $ fedoraPersonCreationDate )
MAY (fedoraPersonIrcNick $ fedoraPersonApprovalStatus $ fedoraPersonBugzillaMail $ fedoraPersonKeyId )
)

View file

@ -1,66 +0,0 @@
#this is the fedoraMembership shema file for use with the Account system
#check out http://www.openldap.org/doc/admin23/schema.html#Extending%20Schema
#for more information
#for now this is the contents
#role_type
#role_domain
#role_status
#internal_comments
#sponsor (points to the sponsor's DN)
#creation (date)
#approval (date)
#don't have a name use cn instead
#attributeType ( 2.5.444.1 NAME 'name'
# DESC 'group name'
# EQUALITY caseIgnoreMatch
# SUBSTR caseIgnoreSubstringsMatch
# SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{50} )
attributeType ( 2.5.444.2 NAME 'fedoraRoleType'
DESC 'the type of role'
EQUALITY caseIgnoreMatch
SUBSTR caseIgnoreSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{25} )
attributeType ( 2.5.444.15 NAME 'fedoraRoleDomain'
DESC 'the domain of this role'
EQUALITY caseIgnoreMatch
SUBSTR caseIgnoreSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{15} )
attributeType ( 2.5.444.3 NAME 'fedoraRoleStatus'
DESC 'the approval status of this role'
EQUALITY caseIgnoreMatch
SUBSTR caseIgnoreSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{15} )
#attributeType ( 2.5.444.4 NAME 'internalComments'
# DESC 'group membership comments'
# SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{1000} )
attributeType ( 2.5.444.5 NAME 'fedoraRoleSponsor'
DESC 'role sponsor'
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{25} )
attributeType ( 2.5.444.6 NAME 'fedoraRoleCreationDate'
DESC 'membership creation date'
EQUALITY caseIgnoreIA5Match
SUBSTR caseIgnoreIA5SubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
attributeType ( 2.5.444.7 NAME 'fedoraRoleApprovalDate'
DESC 'membership approval date'
EQUALITY caseIgnoreIA5Match
SUBSTR caseIgnoreIA5SubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
objectclass ( 2.5.555.2
NAME 'fedoraRole'
DESC 'An object describing a persons roles with the fedora project'
STRUCTURAL
MAY ( fedoraRoleApprovalDate $ fedoraRoleCreationDate $ fedoraRoleType $ fedoraRoleStatus $ fedoraRoleDomain $ fedoraRoleSponsor )
MUST ( cn )
)

1240
fas/po/fas.pot Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -5,10 +5,12 @@
# Extraction from Genshi HTML and text templates
[genshi: **/templates/**.html]
ignore_tags = script,style
include_attrs = alt title summary
extract_text = False
#ignore_tags = script,style
#include_attrs = alt title summary
[genshi: **/templates/**.txt]
template_class = genshi.template:TextTemplate
encoding = UTF-8
#[genshi: **/templates/**.txt]
#template_class = genshi.template:TextTemplate
#extract_text = False
#encoding = UTF-8