diff --git a/fas/fas/auth.py b/fas/fas/auth.py
new file mode 100644
index 0000000..fa59714
--- /dev/null
+++ b/fas/fas/auth.py
@@ -0,0 +1,52 @@
+from turbogears import config
+
+from fas.fasLDAP import UserAccount
+from fas.fasLDAP import Person
+from fas.fasLDAP import Groups
+from fas.fasLDAP import UserGroup
+
+ADMINGROUP = config.get('admingroup')
+
+def isAdmin(userName, g=None):
+ if not g:
+ g = Groups.byUserName(userName)
+ try:
+ g[ADMINGROUP].cn
+ return True
+ except KeyError:
+ return False
+
+def canAdminGroup(userName, groupName, g=None):
+ # TODO: Allow the group owner to admin a group.
+ if not g:
+ g = Groups.byUserName(userName)
+ try:
+ if isAdmin(userName, g) or \
+ (g[groupName].fedoraRoleType.lower() == 'administrator'):
+ return True
+ else:
+ return False
+ except:
+ return False
+
+def canSponsorGroup(userName, groupName, g=None):
+ if not g:
+ g = Groups.byUserName(userName)
+ try:
+ if isAdmin(userName, g) or \
+ canAdminGroup(userName, groupName, g) or \
+ (g[groupName].fedoraRoleType.lower() == 'sponsor'):
+ return True
+ else:
+ return False
+ except:
+ return False
+
+def canEditUser(userName, editUserName):
+ if userName == editUserName:
+ return True
+ elif isAdmin(userName):
+ return True
+ else:
+ return False
+
diff --git a/fas/fas/config/app.cfg b/fas/fas/config/app.cfg
index 6e35357..e3a7147 100644
--- a/fas/fas/config/app.cfg
+++ b/fas/fas/config/app.cfg
@@ -13,6 +13,7 @@ admingroup = 'accounts'
# which view (template engine) to use if one is not specified in the
# template name
# tg.defaultview = "kid"
+tg.defaultview = "genshi"
# The following kid settings determine the settings used by the kid serializer.
diff --git a/fas/fas/controllers.py b/fas/fas/controllers.py
index 9cebfd5..5209768 100644
--- a/fas/fas/controllers.py
+++ b/fas/fas/controllers.py
@@ -10,106 +10,34 @@ from turbogears import exception_handler
import turbogears
import ldap
import time
+from operator import itemgetter
+
+from fas.user import User
+from fas.group import Group
+
# from fas import json
# import logging
# log = logging.getLogger("fas.controllers")
-ADMINGROUP = config.get('admingroup')
-
-class knownUser(validators.FancyValidator):
- def _to_python(self, value, state):
- return value.strip()
- def validate_python(self, value, state):
- p = Person.byUserName(value)
- if p.cn:
- raise validators.Invalid(_("'%s' already exists") % value, value, state)
-
-class unknownUser(validators.FancyValidator):
- def _to_python(self, value, state):
- return value.strip()
- def validate_python(self, value, state):
- p = Person.byUserName(value)
- if not p.cn:
- raise validators.Invalid(_("'%s' does not exist") % value, value, state)
-
-class unknownGroup(validators.FancyValidator):
- def _to_python(self, value, state):
- return value.strip()
- def validate_python(self, value, state):
- g = Groups.groups(groupName)
- if not g:
- raise validators.Invalid(_("'%s' does not exist") % value, value, state)
-
-
-class newPerson(widgets.WidgetsList):
-# cn = widgets.TextField(label='Username', validator=validators.PlainText(not_empty=True, max=10))
- cn = widgets.TextField(label=_('Username'), validator=validators.All(knownUser(not_empty=True, max=10), validators.String(max=32, min=3)))
- givenName = widgets.TextField(label=_('Full Name'), validator=validators.String(not_empty=True, max=42))
- mail = widgets.TextField(label=_('Email'), validator=validators.Email(not_empty=True, strip=True))
- telephoneNumber = widgets.TextField(label=_('Telephone Number'), validator=validators.PhoneNumber(not_empty=True))
- postalAddress = widgets.TextArea(label=_('Postal Address'), validator=validators.NotEmpty)
-
-newPersonForm = widgets.ListForm(fields=newPerson(), submit_text=_('Sign Up'))
-
-class editPerson(widgets.WidgetsList):
-# cn = widgets.TextField(label='Username', validator=validators.PlainText(not_empty=True, max=10))
- userName = widgets.HiddenField(validator=validators.All(unknownUser(not_empty=True, max=10), validators.String(max=32, min=3)))
- givenName = widgets.TextField(label=_('Full Name'), validator=validators.String(not_empty=True, max=42))
- mail = widgets.TextField(label=_('Email'), validator=validators.Email(not_empty=True, strip=True))
- fedoraPersonBugzillaMail = widgets.TextField(label=_('Bugzilla Email'), validator=validators.Email(not_empty=True, strip=True))
- fedoraPersonIrcNick = widgets.TextField(label=_('IRC Nick'))
- fedoraPersonKeyId = widgets.TextField(label=_('PGP Key'))
- telephoneNumber = widgets.TextField(label=_('Telephone Number'), validator=validators.PhoneNumber(not_empty=True))
- postalAddress = widgets.TextArea(label=_('Postal Address'), validator=validators.NotEmpty)
- description = widgets.TextArea(label=_('Description'))
-
-editPersonForm = widgets.ListForm(fields=editPerson(), submit_text=_('Update'))
-
-class editGroup(widgets.WidgetsList):
- groupName = widgets.HiddenField(validator=validators.All(unknownGroup(not_empty=True, max=10), validators.String(max=32, min=3)))
- fedoraGroupDesc = widgets.TextField(label=_('Description'), validator=validators.NotEmpty)
- fedoraGroupOwner = widgets.TextField(label=_('Group Owner'), validator=validators.All(knownUser(not_empty=True, max=10), validators.String(max=32, min=3)))
- fedoraGroupNeedsSponsor = widgets.CheckBox(label=_('Needs Sponsor'))
- fedoraGroupUserCanRemove = widgets.CheckBox(label=_('Self Removal'))
- fedoraGroupJoinMsg = widgets.TextField(label=_('Group Join Message'))
-
-editGroupForm = widgets.ListForm(fields=editGroup(), submit_text=_('Update'))
-
-class findUser(widgets.WidgetsList):
- userName = widgets.AutoCompleteField(label=_('Username'), search_controller='search', search_param='userName', result_name='people')
- action = widgets.HiddenField(default='apply', validator=validators.String(not_empty=True))
- groupName = widgets.HiddenField(validator=validators.String(not_empty=True))
-
-searchUserForm = widgets.ListForm(fields=findUser(), submit_text=_('Invite'))
-
-
class Root(controllers.RootController):
- @expose(template="fas.templates.error")
- def errorMessage(self, tg_exceptions=None):
- ''' Generic exception handler'''
- # Maybe add a popup or alert or some damn thing.
- message = '%s' % tg_exceptions
- return dict(handling_value=True,exception=message)
+
+ user = User()
+ group = Group()
@expose(template="fas.templates.welcome")
# @identity.require(identity.in_group("admin"))
def index(self):
- # log.debug("Happy TurboGears Controller Responding For Duty")
if turbogears.identity.not_anonymous():
turbogears.redirect('home')
return dict(now=time.ctime())
@expose(template="fas.templates.home")
+ @identity.require(identity.not_anonymous())
def home(self):
from feeds import Koji
builds = Koji(turbogears.identity.current.user_name)
return dict(builds=builds)
- @expose(template="fas.templates.dump", format="plain", content_type="text/plain")
- def groupDump(self, groupName=None):
- groups = Groups.byGroupName(groupName)
- return dict(groups=groups, Person=Person)
-
@expose(template="fas.templates.login")
def login(self, forward_url=None, previous_url=None, *args, **kw):
@@ -143,408 +71,23 @@ class Root(controllers.RootController):
turbogears.flash(_('You have successfully logged out.'))
raise redirect("/")
- @expose(template="fas.templates.viewAccount")
- @identity.require(identity.not_anonymous())
- def viewAccount(self,userName=None, action=None):
- if not userName:
- userName = turbogears.identity.current.user_name
- if turbogears.identity.current.user_name == userName:
- personal = True
- else:
- personal = False
- try:
- Groups.byUserName(turbogears.identity.current.user_name)[ADMINGROUP].cn
- admin = True
- except KeyError:
- admin = False
- user = Person.byUserName(userName)
- groups = Groups.byUserName(userName)
- groupsPending = Groups.byUserName(userName, unapprovedOnly=True)
- groupdata={}
- for g in groups:
- groupdata[g] = Groups.groups(g)[g]
- for g in groupsPending:
- groupdata[g] = Groups.groups(g)[g]
- try:
- groups['cla_done']
- claDone=True
- except KeyError:
- claDone=None
- return dict(user=user, groups=groups, groupsPending=groupsPending, action=action, groupdata=groupdata, claDone=claDone, personal=personal, admin=admin)
-
- @expose(template="fas.templates.editAccount")
- @identity.require(identity.not_anonymous())
- def editAccount(self, userName=None, action=None):
- if userName:
- try:
- Groups.byUserName(turbogears.identity.current.user_name)[ADMINGROUP].cn
- if not userName:
- userName = turbogears.identity.current.user_name
- except KeyError:
- turbogears.flash(_('You cannot edit %s') % userName )
- userName = turbogears.identity.current.user_name
- else:
- userName = turbogears.identity.current.user_name
- user = Person.byUserName(userName)
- value = {'userName' : userName,
- 'givenName' : user.givenName,
- 'mail' : user.mail,
- 'fedoraPersonBugzillaMail' : user.fedoraPersonBugzillaMail,
- 'fedoraPersonIrcNick' : user.fedoraPersonIrcNick,
- 'fedoraPersonKeyId' : user.fedoraPersonKeyId,
- 'telephoneNumber' : user.telephoneNumber,
- 'postalAddress' : user.postalAddress,
- 'description' : user.description, }
- return dict(form=editPersonForm, value=value)
-
- @expose(template="fas.templates.viewGroup")
- @exception_handler(errorMessage,rules="isinstance(tg_exceptions,ValueError)")
- @identity.require(identity.not_anonymous())
- def viewGroup(self, groupName):
- try:
- groups = Groups.byGroupName(groupName, includeUnapproved=True)
- except KeyError:
- raise ValueError, _('Group: %s - Does not exist!') % groupName
- try:
- group = Groups.groups(groupName)[groupName]
- except TypeError:
- raise ValueError, _('Group: %s - Does not exist!') % groupName
- userName = turbogears.identity.current.user_name
- try:
- myStatus = groups[userName].fedoraRoleStatus
- except KeyError:
- # Not in group
- myStatus = 'Not a Member' # This has say 'Not a Member'
- except TypeError:
- groups = {}
- try:
- me = groups[userName]
- except:
- me = UserGroup()
- #searchUserForm.groupName.display('group')
- #findUser.groupName.display(value='fff')
- value = {'groupName' : group.cn}
- return dict(groups=groups, group=group, me=me, searchUserForm=searchUserForm, value=value)
-
- @expose(template="fas.templates.editGroup")
- @identity.require(identity.not_anonymous())
- def editGroup(self, groupName, action=None):
- userName = turbogears.identity.current.user_name
- try:
- Groups.byUserName(userName)[ADMINGROUP].cn
- except KeyError:
- try:
- Groups.byUserName(userName)[groupName]
- if Groups.byUserName(userName)[groupName].fedoraRoleType.lower() != 'administrator':
- raise KeyError
- except KeyError:
- turbogears.flash(_('You cannot edit %s') % groupName)
- turbogears.redirect('viewGroup?groupName=%s' % groupName)
- group = Groups.groups(groupName)[groupName]
- value = {'groupName' : groupName,
- 'fedoraGroupOwner' : group.fedoraGroupOwner,
- 'fedoraGroupType' : group.fedoraGroupType,
- 'fedoraGroupNeedsSponsor' : (group.fedoraGroupNeedsSponsor.upper() == 'TRUE'),
- 'fedoraGroupUserCanRemove' : (group.fedoraGroupUserCanRemove.upper() == 'TRUE'),
- 'fedoraGroupJoinMsg' : group.fedoraGroupJoinMsg,
- 'fedoraGroupDesc' : group.fedoraGroupDesc, }
- #'fedoraGroupRequires' : group.fedoraGroupRequires, }
- return dict(form=editGroupForm, value=value)
-
- @expose(template="fas.templates.groupList")
- @exception_handler(errorMessage,rules="isinstance(tg_exceptions,ValueError)")
- @identity.require(identity.not_anonymous())
- def listGroup(self, search='*'):
- groups = Groups.groups(search)
- userName = turbogears.identity.current.user_name
- myGroups = Groups.byUserName(userName)
- try:
- groups.keys()
- except:
- turbogears.flash(_("No Groups found matching '%s'") % search)
- groups = {}
- return dict(groups=groups, search=search, myGroups=myGroups)
-
- @expose(template="fas.templates.resetPassword")
- @exception_handler(errorMessage,rules="isinstance(tg_exceptions,ValueError)")
- def resetPassword(self, userName=None, password=None, passwordCheck=None, mail=None):
- import turbomail
-
- # Logged in
- if turbogears.identity.current.user_name and not password:
- return dict()
-
- # Not logged in
- if not (userName and mail) and not turbogears.identity.current.user_name:
-# turbogears.flash('Please provide your username and password')
- return dict()
-
- if turbogears.identity.current.user_name:
- userName = turbogears.identity.current.user_name
- p = Person.byUserName(userName)
-
- if password and passwordCheck:
- if not password == passwordCheck:
- turbogears.flash(_('Passwords do not match!'))
- return dict()
- if len(password) < 8:
- turbogears.flash(_('Password is too short. Must be at least 8 characters long'))
- return dict()
- newpass = p.generatePassword(password)
-
- if userName and mail and not turbogears.identity.current.user_name:
- if not mail == p.mail:
- turbogears.flash(_("username + email combo unknown."))
- return dict()
- newpass = p.generatePassword()
- message = turbomail.Message('accounts@fedoraproject.org', p.mail, _('Fedora Project Password Reset'))
- message.plain = _("You have requested a password reset! Your new password is - %s \nPlease go to https://admin.fedoraproject.org/fas/ to change it") % newpass['pass']
- turbomail.enqueue(message)
- p.__setattr__('userPassword', newpass['hash'])
-
- p.userPassword = newpass['hash']
- print "PASS: %s" % newpass['pass']
-
- if turbogears.identity.current.user_name:
- turbogears.flash(_("Password Changed"))
- turbogears.redirect("viewAccount")
- else:
- turbogears.flash(_('Your password has been emailed to you'))
- return dict()
-
- @expose(template="fas.templates.userList")
- @exception_handler(errorMessage,rules="isinstance(tg_exceptions,ValueError)")
- @identity.require(identity.in_group("accounts"))
- def listUser(self, search='a*'):
- users = Person.users(search)
- try:
- users[0]
- except:
- turbogears.flash(_("No users found matching '%s'") % search)
- users = []
- cla_done = Groups.byGroupName('cla_done')
- claDone = {}
- for u in users:
- try:
- cla_done[u]
- claDone[u] = True
- except KeyError:
- claDone[u] = False
- return dict(users=users, claDone=claDone, search=search)
-
- listUsers = listUser
-
-# @expose(template='fas.templates.apply')
-# @exception_handler(errorMessage, rules="isinstance(tg_exceptions,ValueError)")
-# @identity.require(identity.not_anonymous())
-# def sudo(self, userName):
-# # This doesn't work
-# turbogears.identity.current.user_name=userName
-# turbogears.flash('Sudoed to %s' % userName)
-# turbogears.recirect('viewAccount')
-
-# @error_handler(viewGroup)
-# @validate(form=newPersonForm)
- @expose(template='fas.templates.apply')
- @identity.require(identity.not_anonymous())
- def modifyGroup(self, groupName, action, userName, **kw):
- ''' Modifies group based on action, groupName and userName '''
- try:
- userName = userName['text']
- except TypeError:
- pass
-
- sponsor = turbogears.identity.current.user_name
- try:
- group = Groups.groups(groupName)[groupName]
- except KeyError:
- turbogears.flash(_('Group Error: %s does not exist.') % groupName)
- turbogears.redirect('viewGroup?groupName=%s' % group.cn)
- try:
- p = Person.byUserName(userName)
- if not p.cn:
- raise KeyError, userName
- except KeyError:
- turbogears.flash(_('User Error: User %s does not exist.') % userName)
- turbogears.redirect('viewGroup?groupName=%s' % group.cn)
-
- g = Groups.byGroupName(groupName, includeUnapproved=True)
-
- # Apply user to a group (as in application)
- if action == 'apply':
- try:
- Groups.apply(groupName, userName)
- except ldap.ALREADY_EXISTS:
- turbogears.flash(_('%s Already in group!') % p.cn)
- turbogears.redirect('viewGroup?groupName=%s' % group.cn)
- else:
- turbogears.flash(_('%s Applied!') % p.cn)
- turbogears.redirect('viewGroup?groupName=%s' % group.cn)
-
- # Some error checking for the sponsors
- if g[userName].fedoraRoleType.lower() == 'administrator' and g[sponsor].fedoraRoleType.lower() == 'sponsor':
- raise ValueError, _('Sponsors cannot alter administrators. End of story.')
-
- try:
- userGroup = Groups.byGroupName(groupName)[userName]
- except KeyError:
- # User not already in the group (happens when users apply for a group)
- userGroup = UserGroup()
- pass
-
- # Remove user from a group
- if action == 'remove':
- try:
- Groups.remove(group.cn, p.cn)
- except TypeError:
- turbogears.flash(_('%(name)s could not be removed from %(group)s!') % {'name' : p.cn, 'group' : group.cn})
- turbogears.redirect('viewGroup?groupName=%s' % group.cn)
- else:
- turbogears.flash(_('%(name)s removed from %(group)s!') % {'name' : p.cn, 'group' : group.cn})
- turbogears.redirect('viewGroup?groupName=%s' % group.cn)
- return dict()
-
- # Upgrade user in a group
- elif action == 'upgrade':
- if g[userName].fedoraRoleType.lower() == 'sponsor' and g[sponsor].fedoraRoleType.lower() == 'sponsor':
- raise ValueError, _('Sponsors cannot admin other sponsors')
- try:
- p.upgrade(groupName)
- except TypeError, e:
- turbogears.flash(_('Cannot upgrade %(name)s - %(error)s!') % {'name' : p.cn, 'error' : e})
- turbogears.redirect('viewGroup?groupName=%s' % group.cn)
- turbogears.flash(_('%s Upgraded!') % p.cn)
- turbogears.redirect('viewGroup?groupName=%s' % group.cn)
-
-
- # Downgrade user in a group
- elif action == 'downgrade':
- if g[userName].fedoraRoleType.lower() == 'administrator' and g[sponsor].fedoraRoleType.lower() == 'sponsor':
- raise ValueError, _('Sponsors cannot downgrade admins')
- try:
- p.downgrade(groupName)
- except TypeError, e:
- turbogears.flash(_('Cannot downgrade %(name)s - %(error)s!') % {'name' : p.cn, 'error' : e})
- turbogears.redirect('viewGroup?groupName=%s' % group.cn)
- turbogears.flash(_('%s Downgraded!') % p.cn)
- turbogears.redirect('viewGroup?groupName=%s' % group.cn)
-
- # Sponsor / Approve User
- elif action == 'sponsor' or action == 'apply':
- p.sponsor(groupName, sponsor)
- turbogears.flash(_('%s has been sponsored!') % p.cn)
- turbogears.redirect('viewGroup?groupName=%s' % group.cn)
-
- turbogears.flash(_('Invalid action: %s') % action)
- turbogears.redirect('viewGroup?groupName=%s' % group.cn)
- return dict()
-
+ ## TODO: Invitation cleanup- move out and validate!
@expose(template='fas.templates.inviteMember')
- @exception_handler(errorMessage,rules="isinstance(tg_exceptions,ValueError)")
@identity.require(identity.not_anonymous())
def inviteMember(self, name=None, email=None, skills=None):
if name and email:
- turbogears.flash(_('Invitation Sent to: "%(name)s" <%(email)s>') % {'name' : name, 'email' : email})
+ turbogears.flash(_('Invitation Sent to: "%(name)s" <%(email)s>') % {'name': name, 'email': email})
if name or email:#FIXME
turbogears.flash(_('Please provide both an email address and the persons name.'))
return dict()
- @expose(template='fas.templates.apply')
- @exception_handler(errorMessage,rules="isinstance(tg_exceptions,ValueError)")
- @identity.require(identity.not_anonymous())
- def applyForGroup(self, groupName, action=None, requestField=None):
- userName = turbogears.identity.current.user_name
-
- group = Groups.groups(groupName)[groupName]
- user = Person.byUserName(userName)
- if action != 'Remove':
- try:
- Groups.apply(groupName, userName)
- turbogears.flash(_('Application sent for %s') % user.cn)
- except ldap.ALREADY_EXISTS, e:
- turbogears.flash(_('Application Denied: %s') % e[0]['desc'])
- turbogears.redirect('viewGroup?groupName=%s' % group.cn)
-
- if action == 'Remove' and group.fedoraGroupUserCanRemove == 'TRUE':
- try:
- Groups.remove(group.cn, user.cn)
- except TypeError:
- turbogears.flash(_('%(user)s could not be removed from %(group)s!') % {'user' : user.cn, 'group' : group.cn})
- turbogears.redirect('viewGroup?groupName=%s' % group.cn)
- else:
- turbogears.flash(_('%(user)s removed from %(group)s!') % {'user' : user.cn, 'group' : group.cn})
- turbogears.redirect('viewGroup?groupName=%s' % group.cn)
- else:
- turbogears.flash(_('%s does not allow self removal') % group.cn)
- turbogears.redirect('viewGroup?groupName=%s' % group.cn)
- return dict()
-
- @expose(template='fas.templates.signUp')
- def signUp(self):
- if turbogears.identity.not_anonymous():
- turbogears.flash(_('No need to sign up, You have an account!'))
- turbogears.redirect('viewAccount')
- return dict(form=newPersonForm)
-
- @validate(form=newPersonForm)
- @error_handler(signUp)
- @expose(template='fas.templates.signUp')
- def newAccountSubmit(self, cn, givenName, mail, telephoneNumber, postalAddress):
- import turbomail
- try:
- Person.newPerson(cn.encode('utf8'), givenName.encode('utf8'), mail.encode('utf8'), telephoneNumber.encode('utf8'), postalAddress.encode('utf8'))
- p = Person.byUserName(cn.encode('utf8'))
- newpass = p.generatePassword()
- message = turbomail.Message('accounts@fedoraproject.org', p.mail, _('Fedora Project Password Reset'))
- message.plain = _("You have requested a password reset! Your new password is - %s \nPlease go to https://admin.fedoraproject.org/fas/ to change it") % newpass['pass']
- turbomail.enqueue(message)
- p.__setattr__('userPassword', newpass['hash'])
- turbogears.flash(_('Your password has been emailed to you. Please log in with it and change your password'))
- turbogears.redirect('/')
-
- except ldap.ALREADY_EXISTS:
- turbogears.flash(_('%s Already Exists, Please pick a different name') % cn)
- turbogears.redirect('signUp')
- return dict()
-
- @validate(form=editPersonForm)
- @error_handler(editAccount)
- @expose(template='fas.templates.editAccount')
- def editAccountSubmit(self, givenName, mail, fedoraPersonBugzillaMail, telephoneNumber, postalAddress, userName=None, fedoraPersonIrcNick='', fedoraPersonKeyId='', description=''):
- if userName:
- try:
- Groups.byUserName(turbogears.identity.current.user_name)[ADMINGROUP].cn
- if not userName:
- userName = turbogears.identity.current.user_name
- except KeyError:
- turbogears.flash(_('You cannot view %s') % userName)
- userName = turbogears.identity.current.user_name
- turbogears.redirect("editAccount")
- return dict()
- else:
- userName = turbogears.identity.current.user_name
- user = Person.byUserName(userName)
- user.__setattr__('givenName', givenName.encode('utf8'))
- user.__setattr__('mail', mail.encode('utf8'))
- user.__setattr__('fedoraPersonBugzillaMail', fedoraPersonBugzillaMail.encode('utf8'))
- user.__setattr__('fedoraPersonIrcNick', fedoraPersonIrcNick.encode('utf8'))
- user.__setattr__('fedoraPersonKeyId', fedoraPersonKeyId.encode('utf8'))
- user.__setattr__('telephoneNumber', telephoneNumber.encode('utf8'))
- user.__setattr__('postalAddress', postalAddress.encode('utf8'))
- user.__setattr__('description', description.encode('utf8'))
- turbogears.flash(_('Your account has been updated.'))
- turbogears.redirect("viewAccount?userName=%s" % userName)
- return dict()
-
@expose(format="json")
def search(self, userName=None, groupName=None):
people = Person.users('%s*' % userName)
return dict(people=
filter(lambda item: userName in item.lower(), people))
-
@expose(template='fas.templates.invite')
- @exception_handler(errorMessage,rules="isinstance(tg_exceptions,ValueError)")
@identity.require(identity.not_anonymous())
def invite(self, target=None):
import turbomail
@@ -566,7 +109,7 @@ 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. \n\
\n\
-Fedora and FOSS are changing the world -- come be a part of it!") % {'name' : user.givenName, 'email' : user.mail}
+Fedora and FOSS are changing the world -- come be a part of it!") % {'name': user.givenName, 'email': user.mail}
turbomail.enqueue(message)
turbogears.flash(_('Message sent to: %s') % target)
return dict(target=target, user=user)
@@ -574,4 +117,3 @@ Fedora and FOSS are changing the world -- come be a part of it!") % {'name' : us
def relativeUser(realUser, sudoUser):
''' Takes user and sees if they are allow to sudo for remote group'''
p = Person.byUserName('realUser')
-
diff --git a/fas/fas/group.py b/fas/fas/group.py
new file mode 100644
index 0000000..f97b8fb
--- /dev/null
+++ b/fas/fas/group.py
@@ -0,0 +1,280 @@
+import turbogears
+from turbogears import controllers, expose, paginate, identity, redirect, widgets, validate, validators, error_handler
+
+import ldap
+
+from fas.fasLDAP import UserAccount
+from fas.fasLDAP import Person
+from fas.fasLDAP import Groups
+from fas.fasLDAP import UserGroup
+
+from fas.auth import isAdmin, canAdminGroup, canSponsorGroup, canEditUser
+
+from operator import itemgetter
+
+from fas.user import knownUser
+
+class knownGroup(validators.FancyValidator):
+ def _to_python(self, value, state):
+ return value.strip()
+ def validate_python(self, value, state):
+ g = Groups.groups(groupName)
+ if g:
+ raise validators.Invalid(_("The group '%s' already exists") % value, value, state)
+
+class unknownGroup(validators.FancyValidator):
+ def _to_python(self, value, state):
+ return value.strip()
+ def validate_python(self, value, state):
+ g = Groups.groups(groupName)
+ if not g:
+ raise validators.Invalid(_("The group '%s' does not exist") % value, value, state)
+
+class createGroup(widgets.WidgetsList):
+ groupName = widgets.TextField(label=_('Group Name'), validator=validators.All(knownGroup(not_empty=True, max=10), validators.String(max=32, min=3)))
+ fedoraGroupDesc = widgets.TextField(label=_('Description'), validator=validators.NotEmpty)
+ fedoraGroupOwner = widgets.TextField(label=_('Group Owner'), validator=validators.All(knownUser(not_empty=True, max=10), validators.String(max=32, min=3)))
+ fedoraGroupNeedsSponsor = widgets.CheckBox(label=_('Needs Sponsor'))
+ fedoraGroupUserCanRemove = widgets.CheckBox(label=_('Self Removal'))
+ fedoraGroupJoinMsg = widgets.TextField(label=_('Group Join Message'))
+
+createGroupForm = widgets.ListForm(fields=createGroup(), submit_text=_('Create'))
+
+class editGroup(widgets.WidgetsList):
+ groupName = widgets.HiddenField(validator=validators.All(unknownGroup(not_empty=True, max=10), validators.String(max=32, min=3)))
+ fedoraGroupDesc = widgets.TextField(label=_('Description'), validator=validators.NotEmpty)
+ fedoraGroupOwner = widgets.TextField(label=_('Group Owner'), validator=validators.All(knownUser(not_empty=True, max=10), validators.String(max=32, min=3)))
+ fedoraGroupNeedsSponsor = widgets.CheckBox(label=_('Needs Sponsor'))
+ fedoraGroupUserCanRemove = widgets.CheckBox(label=_('Self Removal'))
+ fedoraGroupJoinMsg = widgets.TextField(label=_('Group Join Message'))
+
+editGroupForm = widgets.ListForm(fields=editGroup(), submit_text=_('Update'))
+
+class findUser(widgets.WidgetsList):
+ userName = widgets.AutoCompleteField(label=_('Username'), search_controller='search', search_param='userName', result_name='people')
+ action = widgets.HiddenField(default='apply', validator=validators.String(not_empty=True))
+ groupName = widgets.HiddenField(validator=validators.String(not_empty=True))
+
+findUserForm = widgets.ListForm(fields=findUser(), submit_text=_('Invite'))
+
+class Group(controllers.Controller):
+
+ def __init__(self):
+ '''Create a Group Controller.'''
+
+ def index(self):
+ '''Perhaps show a nice explanatory message about groups here?'''
+ return dict()
+
+ @expose(template="fas.templates.group.view")
+ @identity.require(turbogears.identity.not_anonymous())
+ def view(self, groupName):
+ '''View group'''
+ # FIXME: Cleaner checks
+ try:
+ groups = Groups.byGroupName(groupName, includeUnapproved=True)
+ except KeyError:
+ raise ValueError, _('Group: %s - Does not exist!') % groupName
+ try:
+ group = Groups.groups(groupName)[groupName]
+ except TypeError:
+ raise ValueError, _('Group: %s - Does not exist!') % groupName
+ userName = turbogears.identity.current.user_name
+ try:
+ myStatus = groups[userName].fedoraRoleStatus
+ except KeyError:
+ # Not in group
+ myStatus = 'Not a Member' # This has say 'Not a Member'
+ except TypeError:
+ groups = {}
+ try:
+ me = groups[userName]
+ except:
+ me = UserGroup()
+ #searchUserForm.groupName.display('group')
+ #findUser.groupName.display(value='fff')
+ value = {'groupName': group.cn}
+ groups = sorted(groups.items(), key=itemgetter(0))
+ return dict(userName=userName, groups=groups, group=group, me=me, value=value)
+
+ @expose(template="fas.templates.group.new")
+ @identity.require(turbogears.identity.not_anonymous())
+ def new(self, groupName):
+ '''Create a group'''
+ return dict()
+
+ #@validate(form=createGroupForm)
+ @expose(template="fas.templates.group.new")
+ @identity.require(turbogears.identity.not_anonymous())
+ def create(self, groupName, fedoraGroupDesc, fedoraGroupOwner, fedoraGroupNeedsSponsor=True, fedoraGroupUserCanRemove=True, fedoraGroupJoinMsg=""):
+ userName = turbogears.identity.current.user_name
+ if not isAdmin(userName):
+ turbogears.flash(_('Only FAS adminstrators can create groups.'))
+ # TODO: Create a general access denied/error page.
+ turbogears.redirect('/')
+ try:
+ Groups.newGroup(groupName, fedoraGroupDesc, fedoraGroupOwner, fedoraGroupNeedsSponsor, fedoraGroupUserCanRemove, fedoraGroupJoinMsg)
+ turbogears.flash(_("The group: '%s' has been created.") % groupName)
+ turbogears.redirect('/group/view/%s', groupName)
+ except:
+ turbogears.flash(_("The group: '%s' could not be created.") % groupName)
+ return dict()
+
+ @expose(template="fas.templates.group.edit")
+ @identity.require(turbogears.identity.not_anonymous())
+ def edit(self, groupName):
+ '''Edit a group'''
+ #TODO: Handle the "no such group" case (or maybe create
+ #a generic function to check user/group existence.
+ userName = turbogears.identity.current.user_name
+ if not canAdminGroup(userName, groupName):
+ turbogears.flash(_('You cannot edit %s') % groupName)
+ turbogears.redirect('/group/view/%s' % groupName)
+ group = Groups.groups(groupName)[groupName]
+ value = {'groupName': groupName,
+ 'fedoraGroupOwner': group.fedoraGroupOwner,
+ 'fedoraGroupType': group.fedoraGroupType,
+ 'fedoraGroupNeedsSponsor': (group.fedoraGroupNeedsSponsor.upper() == 'TRUE'),
+ 'fedoraGroupUserCanRemove': (group.fedoraGroupUserCanRemove.upper() == 'TRUE'),
+ 'fedoraGroupJoinMsg': group.fedoraGroupJoinMsg,
+ 'fedoraGroupDesc': group.fedoraGroupDesc, }
+ #'fedoraGroupRequires': group.fedoraGroupRequires, }
+ return dict(value=value)
+
+ #@validate(form=editGroupForm)
+ @expose(template="fas.templates.group.edit")
+ @identity.require(turbogears.identity.not_anonymous())
+ def save(self, stuff):
+ #TODO
+ return dict()
+
+ @expose(template="fas.templates.group.list")
+ @identity.require(turbogears.identity.not_anonymous())
+ def list(self, search='*'):
+ groups = Groups.groups(search)
+ userName = turbogears.identity.current.user_name
+ myGroups = Groups.byUserName(userName)
+ try:
+ groups.keys()
+ except:
+ turbogears.flash(_("No Groups found matching '%s'") % search)
+ groups = {}
+ groups = sorted(groups.items(), key=itemgetter(0))
+ return dict(groups=groups, search=search, myGroups=myGroups)
+
+ # TODO: Validate
+ @expose(template='fas.templates.group.view')
+ @identity.require(turbogears.identity.not_anonymous())
+ def apply(self, groupName, userName):
+ try:
+ Groups.apply(groupName, userName)
+ except ldap.ALREADY_EXISTS:
+ turbogears.flash(_('%(user)s is already in %(group)s!') % {'user': userName, 'group': groupName})
+ turbogears.redirect('/group/view/%s' % groupName)
+ else:
+ turbogears.flash(_('%(user)s has applied to %(group)s!') % {'user': userName, 'group': groupName})
+ turbogears.redirect('/group/view/%s' % group.cn)
+
+ # TODO: Validate (user doesn't exist case)
+ @expose(template='fas.templates.group.view')
+ @identity.require(turbogears.identity.not_anonymous())
+ def sponsor(self, groupName, userName):
+ '''Sponsor user'''
+ sponsor = turbogears.identity.current.user_name
+ if not canSponsorGroup(sponsor, groupName):
+ turbogears.flash(_("You are not a sponsor for '%s'") % groupName)
+ turbogears.redirect('/group/view/%s' % groupName)
+ try:
+ group = Groups.groups(groupName)[groupName]
+ except KeyError:
+ turbogears.flash(_('Group Error: %s does not exist.') % groupName)
+ # The following line is kind of pointless- any suggestions?
+ turbogears.redirect('/group/view/%s' % groupName)
+ p = Person.byUserName(userName)
+ g = Groups.byGroupName(groupName, includeUnapproved=True)
+ # TODO: Check if the person actually applied to the group.
+ p.sponsor(groupName, sponsor)
+ turbogears.flash(_('%s has been sponsored!') % p.cn)
+ turbogears.redirect('/group/view/%s' % groupName)
+
+ # TODO: Validate (user doesn't exist case)
+ @expose(template='fas.templates.group.view')
+ @identity.require(turbogears.identity.not_anonymous())
+ def remove(self, groupName, userName):
+ '''Remove user from group'''
+ # TODO: Add confirmation?
+ sponsor = turbogears.identity.current.user_name
+ if not canSponsorGroup(sponsor, groupName) \
+ and sponsor != userName: # Users can remove themselves
+ turbogears.flash(_("You are not a sponsor for '%s'") % groupName)
+ turbogears.redirect('/group/view/%s' % groupName)
+ if canAdminGroup(userName, groupName) \
+ and (not canAdminGroup(sponsor, groupName)):
+ turbogears.flash(_('Sponsors cannot remove administrators.') % userName)
+ turbogears.redirect('/group/view/%s' % groupName)
+ try:
+ Groups.remove(groupName, userName)
+ except TypeError:
+ turbogears.flash(_('%(name)s could not be removed from %(group)s!') % {'name': userName, 'group': groupName})
+ turbogears.redirect('/group/view/%s' % groupName)
+ else:
+ turbogears.flash(_('%(name)s has been removed from %(group)s!') % {'name': userName, 'group': groupName})
+ turbogears.redirect('/group/view/%s' % groupName)
+ return dict()
+
+ # TODO: Validate (user doesn't exist case)
+ @expose(template='fas.templates.group.view')
+ @identity.require(turbogears.identity.not_anonymous())
+ def upgrade(self, groupName, userName):
+ '''Upgrade user in group'''
+ sponsor = turbogears.identity.current.user_name
+ if not canSponsorGroup(sponsor, groupName):
+ turbogears.flash(_("You are not a sponsor for '%s'") % groupName)
+ turbogears.redirect('/group/view/%s' % groupName)
+ # This is already checked in fasLDAP.py
+ #if canAdminGroup(userName, groupName):
+ # turbogears.flash(_('Group administators cannot be upgraded any further.'))
+ # turbogears.redirect('/group/view/%s' % groupName)
+ elif canSponsorGroup(userName, groupName) \
+ and (not canAdminGroup(sponsor, groupName)):
+ turbogears.flash(_('Sponsors cannot upgrade other sponsors.') % userName)
+ turbogears.redirect('/group/view/%s' % groupName)
+ p = Person.byUserName(userName)
+ try:
+ p.upgrade(groupName)
+ except:
+ turbogears.flash(_('%(name)s could not be upgraded!') % userName)
+ turbogears.redirect('/group/view/%s' % groupName)
+ turbogears.flash(_('%s has been upgraded!') % userName)
+ turbogears.redirect('/group/view/%s' % groupName)
+
+ # TODO: Validate (user doesn't exist case)
+ @expose(template='fas.templates.group.view')
+ @identity.require(turbogears.identity.not_anonymous())
+ def downgrade(self, groupName, userName):
+ '''Upgrade user in group'''
+ sponsor = turbogears.identity.current.user_name
+ if not canSponsorGroup(sponsor, groupName):
+ turbogears.flash(_("You are not a sponsor for '%s'") % groupName)
+ turbogears.redirect('/group/view/%s' % groupName)
+ if canAdminGroup(userName, groupName) \
+ and (not canAdminGroup(sponsor, groupName)):
+ turbogears.flash(_('Sponsors cannot downgrade group administrators.') % userName)
+ turbogears.redirect('/group/view/%s' % groupName)
+ p = Person.byUserName(userName)
+ try:
+ p.upgrade(groupName)
+ except:
+ turbogears.flash(_('%(name)s could not be downgraded!') % userName)
+ turbogears.redirect('/group/view/%s' % groupName)
+ turbogears.flash(_('%s has been downgraded!') % p.cn)
+ turbogears.redirect('/group/view/%s' % groupName)
+
+ # TODO: Validate (group doesn't exist case)
+ @expose(template="genshi-text:fas.templates.group.dump", content_type='text/plain; charset=utf-8')
+ @identity.require(turbogears.identity.not_anonymous())
+ def dump(self, groupName=None):
+ groups = Groups.byGroupName(groupName)
+ groups = sorted(groups.items(), key=itemgetter(0))
+ return dict(groups=groups, Person=Person)
+
diff --git a/fas/fas/static/css/style.css b/fas/fas/static/css/style.css
index 42ec9fc..3ad8559 100644
--- a/fas/fas/static/css/style.css
+++ b/fas/fas/static/css/style.css
@@ -407,30 +407,29 @@ pre
font-size: 3ex;
}
-form ul
+form
{
list-style: none;
margin: 1ex 0!important;
}
-form ul li
+form .field
{
margin: 0 0 1ex;
text-align: left;
overflow: hidden;
}
-form ul label
+form .field label
{
float: left;
clear: left;
width: 16ex;
text-align: right;
- margin: 0;
padding: 0 2ex 0 0;
}
-form ul input, form ul textarea
+form .field input, form .field textarea
{
margin: 0;
}
diff --git a/fas/fas/templates/autoComplete.kid b/fas/fas/templates/autoComplete.kid
deleted file mode 100644
index 7fe9f4a..0000000
--- a/fas/fas/templates/autoComplete.kid
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
diff --git a/fas/fas/templates/dump.kid b/fas/fas/templates/dump.kid
deleted file mode 100644
index 26a1308..0000000
--- a/fas/fas/templates/dump.kid
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
- ${user},${Person.byUserName(user).mail},${Person.byUserName(user).givenName},${groups[user].fedoraRoleType},0
-
-
-
diff --git a/fas/fas/templates/editAccount.kid b/fas/fas/templates/editAccount.kid
deleted file mode 100644
index a5db8aa..0000000
--- a/fas/fas/templates/editAccount.kid
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
- Edit Account
-
-
- Edit Account
- ${form(action='editAccountSubmit', method='post', value=value)}
-
-
diff --git a/fas/fas/templates/editGroup.kid b/fas/fas/templates/editGroup.kid
deleted file mode 100644
index aff7bdc..0000000
--- a/fas/fas/templates/editGroup.kid
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
- Edit Group
-
-
- Edit Group
- ${form(action='editGroupSubmit', method='post', value=value)}
-
-
diff --git a/fas/fas/templates/error.kid b/fas/fas/templates/error.kid
deleted file mode 100644
index 32b89a7..0000000
--- a/fas/fas/templates/error.kid
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
- Crap!
-
-
- ${exception}
-
-
diff --git a/fas/fas/templates/group/__init__.py b/fas/fas/templates/group/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/fas/fas/templates/group/__init__.pyc b/fas/fas/templates/group/__init__.pyc
new file mode 100644
index 0000000..1c33caa
Binary files /dev/null and b/fas/fas/templates/group/__init__.pyc differ
diff --git a/fas/fas/templates/group/dump.txt b/fas/fas/templates/group/dump.txt
new file mode 100644
index 0000000..5fab998
--- /dev/null
+++ b/fas/fas/templates/group/dump.txt
@@ -0,0 +1,3 @@
+#for user in groups
+${user[0].decode('utf-8')},${Person.byUserName(user[0]).mail.decode('utf-8')},${Person.byUserName(user[0]).givenName.decode('utf-8')},${user[1].fedoraRoleType.decode('utf-8')}
+#end
diff --git a/fas/fas/templates/group/edit.html b/fas/fas/templates/group/edit.html
new file mode 100644
index 0000000..42c50f1
--- /dev/null
+++ b/fas/fas/templates/group/edit.html
@@ -0,0 +1,39 @@
+
+
+
+
+ Edit Group
+
+
+ Edit Group
+
+
+
diff --git a/fas/fas/templates/group/list.html b/fas/fas/templates/group/list.html
new file mode 100644
index 0000000..7e424c6
--- /dev/null
+++ b/fas/fas/templates/group/list.html
@@ -0,0 +1,44 @@
+
+
+
+
+ Groups List
+
+
+ List (${search})
+ Search Groups
+
+ Results
+
+
+
+
+
diff --git a/fas/fas/templates/group/new.html b/fas/fas/templates/group/new.html
new file mode 100644
index 0000000..9e7b04c
--- /dev/null
+++ b/fas/fas/templates/group/new.html
@@ -0,0 +1,41 @@
+
+
+
+
+ Create a new FAS Group
+
+
+ Create a new FAS Group
+
+
+
diff --git a/fas/fas/templates/group/view.html b/fas/fas/templates/group/view.html
new file mode 100644
index 0000000..05ca365
--- /dev/null
+++ b/fas/fas/templates/group/view.html
@@ -0,0 +1,87 @@
+
+
+
+
+ Edit Group
+
+
+ ${group.fedoraGroupDesc} (${group.cn})
+
+ My Status:
+ Approved
+ Unapproved
+ Not a Member
+
+
+ Remove me
+ Group Details (edit)
+
+
+ - Name:
- ${group.cn}
+ - Description:
- ${group.fedoraGroupDesc}
+ - Owner:
- ${group.fedoraGroupOwner}
+ - Type:
- ${group.fedoraGroupType}
+ - Needs Sponsor:
-
+ Yes
+ No
+
+ - Self Removal
-
+ Yes
+ No
+
+ - Join Message:
- ${group.fedoraGroupJoinMsg}
+
+
+
+ Members
+
+
+
diff --git a/fas/fas/templates/groupList.kid b/fas/fas/templates/groupList.kid
deleted file mode 100644
index c7a6b45..0000000
--- a/fas/fas/templates/groupList.kid
+++ /dev/null
@@ -1,46 +0,0 @@
-
-
-
- Groups List
-
-
- List (${search})
- Search Groups
-
- Results
-
-
-
-
-
diff --git a/fas/fas/templates/home.kid b/fas/fas/templates/home.html
similarity index 75%
rename from fas/fas/templates/home.kid
rename to fas/fas/templates/home.html
index 1548cdf..ac00ab2 100644
--- a/fas/fas/templates/home.kid
+++ b/fas/fas/templates/home.html
@@ -1,5 +1,8 @@
-
-
+
+
+
Fedora Accounts System
@@ -18,6 +21,5 @@
${builds.builds[build]['pubDate']} |
-
diff --git a/fas/fas/templates/invite.kid b/fas/fas/templates/invite.html
similarity index 83%
rename from fas/fas/templates/invite.kid
rename to fas/fas/templates/invite.html
index 515454a..9e2c1ad 100644
--- a/fas/fas/templates/invite.kid
+++ b/fas/fas/templates/invite.html
@@ -1,11 +1,14 @@
-
-
+
+
+
Invite a new community member!
Invite a new community member!
-