diff --git a/fas/fas/model.py b/fas/fas/model.py index 749aa83..c108d1b 100644 --- a/fas/fas/model.py +++ b/fas/fas/model.py @@ -101,13 +101,21 @@ visit_identity_table = Table('visit_identity', metadata, class People(SABase): '''Records for all the contributors to Fedora.''' - + + def by_id(cls, id): + ''' + A class method that can be used to search users + based on their unique id + ''' + return cls.query.filter_by(id=id).one() + by_id = classmethod(by_id) + def by_email_address(cls, email): ''' A class method that can be used to search users based on their email addresses since it is unique. ''' - return cls.query.join('emails').filter_by(email=email).first() + return cls.query.join(['email_purposes', 'person_email']).filter_by(email=email).one() by_email_address = classmethod(by_email_address) @@ -133,7 +141,7 @@ class People(SABase): role.role_type = 'user' role.member = cls role.group = group - + def approve(cls, group, requester): ''' Approve a person in a group - requester for logging purposes @@ -143,7 +151,7 @@ class People(SABase): else: role = PersonRoles.query.filter_by(member=cls, group=group).one() role.role_status = 'approved' - + def upgrade(cls, group, requester): ''' Upgrade a user in a group - requester for logging purposes @@ -158,7 +166,7 @@ class People(SABase): role.role_type = 'administrator' elif role.role_type == 'user': role.role_type = 'sponsor' - + def downgrade(cls, group, requester): ''' Downgrade a user in a group - requester for logging purposes @@ -173,7 +181,7 @@ class People(SABase): role.role_type = 'user' elif role.role_type == 'administrator': role.role_type = 'sponsor' - + def sponsor(cls, group, requester): # If we want to do logging, this might be the place. if not group in cls.unapproved_memberships: @@ -292,6 +300,24 @@ class Configs(SABase): class Groups(SABase): '''Group that people can belong to.''' + + def by_id(cls, id): + ''' + A class method that can be used to search groups + based on their unique id + ''' + return cls.query.filter_by(id=id).one() + by_id = classmethod(by_id) + + def by_email_address(cls, email): + ''' + A class method that can be used to search groups + based on their email addresses since it is unique. + ''' + return cls.query.join(['group_email_purposes', 'group_email']).filter_by(email=email).one() + + by_email_address = classmethod(by_email_address) + def by_name(cls, name): ''' A class method that permits to search groups @@ -364,7 +390,7 @@ class UnApprovedRoles(PersonRoles): class Visit(SABase): '''Track how many people are visiting the website. - + It doesn't currently make sense for us to track this here so we clear this table of stale records every hour. ''' @@ -374,7 +400,7 @@ class Visit(SABase): class VisitIdentity(SABase): '''Associate a user with a visit cookie. - + This allows users to log in to app. ''' pass @@ -403,7 +429,7 @@ mapper(People, PeopleTable, properties = { primaryjoin = PeopleTable.c.id==UnApprovedRoles.c.person_id) }) mapper(PersonEmails, PersonEmailsTable, properties = { - 'person': relation(People, uselist = False, + 'person': relation(People, backref = 'person_emails', uselist = False, primaryjoin = PeopleTable.c.id==PersonEmailsTable.c.person_id) }) mapper(EmailPurposes, EmailPurposesTable, properties = { @@ -430,7 +456,7 @@ mapper(Groups, GroupsTable, properties = { primaryjoin = GroupsTable.c.prerequisite_id==GroupsTable.c.id) }) mapper(GroupEmails, GroupEmailsTable, properties = { - 'group': relation(Groups, uselist = False, + 'group': relation(Groups, backref = 'group_emails', uselist = False, primaryjoin = GroupsTable.c.id==GroupEmailsTable.c.group_id) }) mapper(GroupEmailPurposes, GroupEmailPurposesTable, properties = { diff --git a/fas/fas/templates/user/email/__init__.py b/fas/fas/templates/user/email/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/fas/fas/templates/user/email/manage.html b/fas/fas/templates/user/email/manage.html new file mode 100644 index 0000000..0d449b1 --- /dev/null +++ b/fas/fas/templates/user/email/manage.html @@ -0,0 +1,47 @@ + + + + + ${_('Manage Emails')} + + +

${_('Managing Emails for %s') % person.username}

+

Available Emails

+ + + + + + + + + + + + + + + + +
${_('Email')}${_('Description')}${_('Verified')}
${email.email}${email.description}${_('Verified')}${_('Unverified')} ${_('Resend Verification')}
+

Set Emails

+ + + + + + + + + + + + + + + +
${_('Email')}${_('Description')}${_('Purpose')}
${purpose.email}${purpose.person_email.description}${purpose.purpose}
+ + diff --git a/fas/fas/user.py b/fas/fas/user.py index 3318393..a86173b 100644 --- a/fas/fas/user.py +++ b/fas/fas/user.py @@ -13,9 +13,11 @@ import subprocess from fas.model import People from fas.model import PersonEmails +from fas.model import EmailPurposes from fas.model import Log from fas.auth import * +from fas.user_email import Email, NonFedoraEmail from random import Random import sha @@ -32,14 +34,6 @@ class KnownUser(validators.FancyValidator): except InvalidRequestError: raise validators.Invalid(_("'%s' does not exist.") % value, value, state) -class NonFedoraEmail(validators.FancyValidator): - '''Make sure that an email address is not @fedoraproject.org''' - def _to_python(self, value, state): - return value.strip() - def validate_python(self, value, state): - if value.endswith('@fedoraproject.org'): - raise validators.Invalid(_("To prevent email loops, your email address cannot be @fedoraproject.org."), value, state) - class UnknownUser(validators.FancyValidator): '''Make sure that a user doesn't already exist''' def _to_python(self, value, state): @@ -62,15 +56,14 @@ class ValidSSHKey(validators.FancyValidator): def validate_python(self, value, state): # value = value.file.read() print dir(value) - email_pattern = "[a-zA-Z0-9\.\+\-_]+@[a-zA-Z0-9\.\-]+" keylines = value.split('\n') print "KEYLINES: %s" % keylines for keyline in keylines: if not keyline: continue keyline = keyline.strip() - m = re.match('ssh-[dr]s[as] [^ ]+ ' + email_pattern, keyline) - if not m or m.end() < len(keyline): + m = re.match('^(rsa|dsa|ssh-rsa|ssh-dss) [ \t]*[^ \t]+.*$', keyline) + if not m: raise validators.Invalid(_('Error - Not a valid ssh key: %s') % keyline, value, state) class ValidUsername(validators.FancyValidator): @@ -153,6 +146,8 @@ def generate_salt(length=8): class User(controllers.Controller): + email = Email() + def __init__(self): '''Create a User Controller. ''' @@ -316,7 +311,6 @@ class User(controllers.Controller): newpass = generate_password() message = turbomail.Message(config.get('accounts_mail'), person.emails['primary'], _('Welcome to the Fedora Project!')) - HERE message.plain = _(''' You have created a new Fedora account! Your new password is: %s diff --git a/fas/fas/user_email.py b/fas/fas/user_email.py new file mode 100644 index 0000000..2ef95c7 --- /dev/null +++ b/fas/fas/user_email.py @@ -0,0 +1,59 @@ +import turbogears +from turbogears import controllers, expose, paginate, identity, redirect, widgets, validate, validators, error_handler, config +from turbogears.database import session +import cherrypy + +from fas.model import People +from fas.model import PersonEmails +from fas.model import EmailPurposes +from fas.model import Log + +class NonFedoraEmail(validators.FancyValidator): + '''Make sure that an email address is not @fedoraproject.org''' + def _to_python(self, value, state): + return value.strip() + def validate_python(self, value, state): + if value.endswith('@fedoraproject.org'): + raise validators.Invalid(_("To prevent email loops, your email address cannot be @fedoraproject.org."), value, state) + +class EmailCreate(validators.Schema): + email = validators.All( + validators.Email(not_empty=True, strip=True), + NonFedoraEmail(not_empty=True, strip=True), + ) + #fedoraPersonBugzillaMail = validators.Email(strip=True) + postal_address = validators.String(max=512) + +class Email(controllers.Controller): + + def __init__(self): + '''Create an Email Controller. + ''' + + @identity.require(turbogears.identity.not_anonymous()) + def index(self): + '''Redirect to manage + ''' + turbogears.redirect('/user/email/manage/%s' % turbogears.identity.current.user_name) + + + @expose(template="fas.templates.error") + def error(self, tg_errors=None): + '''Show a friendly error message''' + if not tg_errors: + turbogears.redirect('/') + return dict(tg_errors=tg_errors) + + @identity.require(turbogears.identity.not_anonymous()) + #@validate(validators=UserView()) + @error_handler(error) + @expose(template="fas.templates.user.email.manage", allow_json=True) + def manage(self, username=None): + ''' + Manage a person's emails. + ''' + if not username: + username = turbogears.identity.current.user_name + person = People.by_username(username) + return dict(person=person) +