def login(self, forward_url=None, *args, **kwargs): '''Page to become authenticated to the Account System. This shows a small login box to type in your username and password from the Fedora Account System. :kwarg forward_url: The url to send to once authentication succeeds ''' login_dict = f_ctrlers.login(forward_url=forward_url, *args, **kwargs) if not identity.current.anonymous and identity.was_login_attempted() \ and not identity.get_identity_errors(): # Success that needs to be passed back via json return login_dict if identity.was_login_attempted() and request.fas_provided_username: if request.fas_identity_failure_reason == 'status_inactive': turbogears.flash(_('Your old password has expired. Please' ' reset your password below.')) if request_format() != 'json': redirect('/user/resetpass') if request.fas_identity_failure_reason == 'status_account_disabled': turbogears.flash(_('Your account is currently disabled. For' ' more information, please contact %(admin_email)s' % {'admin_email': config.get('accounts_email')})) if request_format() != 'json': redirect('/login') return login_dict
def json_redirect(f, *args, **kw): try: return f(*args, **kw) except InvalidUpdateException, e: if request_format() == 'json': return dict() else: raise redirect('/new', **e.args[0])
def index(self): if turbogears.identity.not_anonymous(): if request_format() == 'json': # redirects don't work with JSON calls. This is a bit of a # hack until we can figure out something better. return dict() turbogears.redirect('/home') return dict(now=time.ctime())
def error(self, tg_errors=None): '''Show a friendly error message''' # Only tg controller methods are served via the web so we have to have # methods even if they could be functions. (R0201) # pylint: disable-msg=R0201 ### FIXME: This can be made simpler once we have a new python-fedora # with jsonify_validation_errors() if tg_errors: if request_format() == 'json': message = '\n'.join([u'%s: %s' % (param, msg) for param, msg in tg_errors.items()]) return dict(exc='Invalid', tg_flash=message, tg_template='json') if not tg_errors: # We should only reach this if the user hit the url manually. if request_format() == 'json': return dict() else: redirect('/') return dict(tg_errors=tg_errors)
def name(self, collctn): '''Return a page with information on a particular Collection :arg collctn: Collection shortname ''' ### FIXME: Want to return additional info: # date it was created (join log table: creation date) # The initial import doesn't have this information, though. try: #pylint:disable-msg=E1101 collection = Collection.by_simple_name(collctn) except InvalidRequestError: # Either the name doesn't exist or somehow it references more than # one value flash( _('The collection name you were linked to, %(collctn)s,' ' does not exist. If you received this error from' ' a link on the fedoraproject.org website, please' ' report it.') % {'collctn': collctn}) if request_format() == 'json': error = dict(exc='InvalidCollection') else: error = dict(title=_('%(app)s -- Invalid Collection Name') % {'app': self.app_title}, tg_template='pkgdb.templates.errors') return error # Why do we reformat the data returned from the database? # 1) We don't need all the information in the collection object # 2) We need statusname which is not in the specific table. collection_entry = { 'name': collection.name, 'version': collection.version, 'owner': collection.owner, 'summary': collection.summary, 'description': collection.description, 'statusname': collection.status.locale['C'].statusname } # Retrieve the package list for this collection # pylint:disable-msg=E1101 packages = select((PackageTable, ), and_(Package.id == PackageListing.packageid, PackageListing.collectionid == collection.id, Package.statuscode != STATUS['Removed']), order_by=(Package.name, )).execute() # pylint:enable-msg=E1101 return dict(title='%s -- %s %s' % (self.app_title, collection.name, collection.version), collection=collection_entry, packages=packages)
def login(forward_url=None, *args, **kwargs): '''Page to become authenticated to the Account System. This shows a small login box to type in your username and password from the Fedora Account System. To use this, replace your current login controller method with:: from fedora.controllers import login as fc_login @expose(template='yourapp.yourlogintemplate', allow_json=True) def login(self, forward_url=None, *args, **kwargs): login_dict = fc_login(forward_url, args, kwargs) # Add anything to the return dict that you need for your app return login_dict :kwarg: forward_url: The url to send to once authentication succeeds ''' if forward_url: if isinstance(forward_url, list): forward_url = forward_url.pop(0) else: del request.params['forward_url'] if not identity.current.anonymous and identity.was_login_attempted() \ and not identity.get_identity_errors(): # User is logged in flash(_('Welcome, %s') % identity.current.user_name) if request_format() == 'json': # When called as a json method, doesn't make any sense to redirect # to a page. Returning the logged in identity is better. return dict(user=identity.current.user, _csrf_token=identity.current.csrf_token) redirect(forward_url or '/') if identity.was_login_attempted(): msg = _('The credentials you supplied were not correct or ' 'did not grant access to this resource.') elif identity.get_identity_errors(): msg = _('You must provide your credentials before accessing ' 'this resource.') else: msg = _('Please log in.') if not forward_url: forward_url = request.headers.get('Referer', '/') response.status = 403 return dict(logging_in=True, message=msg, forward_url=forward_url, previous_url=request.path_info, original_parameters=request.params)
def login(forward_url=None, *args, **kwargs): '''Page to become authenticated to the Account System. This shows a small login box to type in your username and password from the Fedora Account System. To use this, replace your current login controller method with:: from fedora.controllers import login as fc_login @expose(template='yourapp.yourlogintemplate', allow_json=True) def login(self, forward_url=None, *args, **kwargs): login_dict = fc_login(forward_url, args, kwargs) # Add anything to the return dict that you need for your app return login_dict :kwarg: forward_url: The url to send to once authentication succeeds ''' if forward_url: if isinstance(forward_url, list): forward_url = forward_url.pop(0) else: del request.params['forward_url'] if not identity.current.anonymous and identity.was_login_attempted() \ and not identity.get_identity_errors(): # User is logged in flash(f_('Welcome, %s') % identity.current.user_name) if request_format() == 'json': # When called as a json method, doesn't make any sense to redirect # to a page. Returning the logged in identity is better. return dict(user=identity.current.user, _csrf_token=identity.current.csrf_token) redirect(forward_url or '/') if identity.was_login_attempted(): msg = f_('The credentials you supplied were not correct or ' 'did not grant access to this resource.') elif identity.get_identity_errors(): msg = f_('You must provide your credentials before accessing ' 'this resource.') else: msg = f_('Please log in.') if not forward_url: forward_url = request.headers.get('Referer', '/') response.status = 403 return dict( logging_in=True, message=msg, forward_url=forward_url, previous_url=request.path_info, original_parameters=request.params )
def default(self, buildName=None, repo='F-11-i386'): '''Retrieve PackageBuild by their name. This method returns general packagebuild/rpm information about a package like: version, release, size, last changelog message, committime etc. This information comes from yum and is stored in the pkgdb. :arg buildName: Name of the packagebuild/rpm to lookup :arg repo: shortname of the repository to look in ''' if buildName == None: raise redirect('/packages/list/') #pylint:disable-msg=E1101 #builds_query = PackageBuild.query.filter_by(name=buildName) #pylint:enable-msg=E1101 # look for The One packagebuild try: #pylint:disable-msg=E1101 build = builds_query.join( PackageBuild.repos).filter(Repo.shortname == repo).one() except: error = dict( status=False, title=_('%(app)s -- Invalid PackageBuild Name') % {'app': self.app_title}, message=_('The package build you were linked to' ' (%(pkg)s) does not appear in the Package ' ' Database. If you received this error from a link' ' on the fedoraproject.org website, please report' ' it.') % {'pkg': buildName}) if request_format() != 'json': error['tg_template'] = 'pkgdb.templates.errors' return error other_repos = [] arches = set() for b in builds_query.all(): other_repos.append(b.repo) arches.add(b.architecture) other_repos.remove(build.repo) return dict(title=_('%(title)s -- %(pkg)s') % { 'title': self.app_title, 'pkg': buildName }, repo=repo, build=build, other_repos=other_repos, arches=arches)
def name(self, collctn): '''Return a page with information on a particular Collection :arg collctn: Collection shortname ''' ### FIXME: Want to return additional info: # date it was created (join log table: creation date) # The initial import doesn't have this information, though. try: #pylint:disable-msg=E1101 collection = Collection.by_simple_name(collctn) except InvalidRequestError: # Either the name doesn't exist or somehow it references more than # one value flash(_('The collection name you were linked to, %(collctn)s,' ' does not exist. If you received this error from' ' a link on the fedoraproject.org website, please' ' report it.') % {'collctn': collctn}) if request_format() == 'json': error = dict(exc='InvalidCollection') else: error = dict(title=_('%(app)s -- Invalid Collection Name') % { 'app': self.app_title}, tg_template='pkgdb.templates.errors') return error # Why do we reformat the data returned from the database? # 1) We don't need all the information in the collection object # 2) We need statusname which is not in the specific table. collection_entry = {'name': collection.name, 'version': collection.version, 'owner': collection.owner, 'summary': collection.summary, 'description': collection.description, 'statusname': collection.status.locale['C'].statusname } # Retrieve the package list for this collection # pylint:disable-msg=E1101 packages = select((PackageTable,), and_(Package.id==PackageListing.packageid, PackageListing.collectionid==collection.id, Package.statuscode!=STATUS['Removed']), order_by=(Package.name,)).execute() # pylint:enable-msg=E1101 return dict(title='%s -- %s %s' % (self.app_title, collection.name, collection.version), collection=collection_entry, packages=packages)
def default(self, buildName=None, repo='F-11-i386'): '''Retrieve PackageBuild by their name. This method returns general packagebuild/rpm information about a package like: version, release, size, last changelog message, committime etc. This information comes from yum and is stored in the pkgdb. :arg buildName: Name of the packagebuild/rpm to lookup :arg repo: shortname of the repository to look in ''' if buildName == None: raise redirect('/packages/list/') #pylint:disable-msg=E1101 #builds_query = PackageBuild.query.filter_by(name=buildName) #pylint:enable-msg=E1101 # look for The One packagebuild try: #pylint:disable-msg=E1101 build = builds_query.join(PackageBuild.repos).filter( Repo.shortname==repo).one() except: error = dict(status=False, title=_('%(app)s -- Invalid PackageBuild Name') % { 'app': self.app_title}, message=_('The package build you were linked to' ' (%(pkg)s) does not appear in the Package ' ' Database. If you received this error from a link' ' on the fedoraproject.org website, please report' ' it.') % {'pkg': buildName}) if request_format() != 'json': error['tg_template'] = 'pkgdb.templates.errors' return error other_repos = [] arches = set() for b in builds_query.all(): other_repos.append(b.repo) arches.add(b.architecture) other_repos.remove(build.repo) return dict(title=_('%(title)s -- %(pkg)s') % { 'title': self.app_title, 'pkg': buildName}, repo=repo, build=build, other_repos=other_repos, arches=arches)
def set(self, username, application, attribute, value=None): '''Retrieve configs for a user and application. Arguments: :username: owner of the configs we're searching for :application: program that the configs are for :attribute: name of the option we're setting :value: value to set in the db. If ths is None, we won't set anything ''' # Only tg controller methods are served via the web so we have to have # methods even if they could be functions. (R0201) # pylint: disable-msg=R0201 # Verify user is allowed to set this config target = People.by_username(username) if not can_edit_user(identity.current.user, target): flash(_('You cannot edit configs for %s') % username) if request_format() == 'json': return dict(exc='AuthorizationError') else: redirect('/') # Retrieve the data and reformat it as a dict keyed on attribute try: # pylint: disable-msg=E1101 config = Configs.query.filter_by( application=application, attribute=attribute).filter( Configs.person_id == target.id).one() config.value = value except InvalidRequestError: # There was no Config, create a new one config = Configs(application=application, attribute=attribute, value=value) # pylint: disable-msg=E1101 config.person = People.query.filter_by(username=username).one() try: # ScopedSession really does have a flush() method # pylint: disable-msg=E1101 session.flush() except DBAPIError, error: flash(_('Error saving the config to the database: %s' % (error))) return dict(exc='DBAPIError')
def set(self, username, application, attribute, value=None): '''Retrieve configs for a user and application. Arguments: :username: owner of the configs we're searching for :application: program that the configs are for :attribute: name of the option we're setting :value: value to set in the db. If ths is None, we won't set anything ''' # Only tg controller methods are served via the web so we have to have # methods even if they could be functions. (R0201) # pylint: disable-msg=R0201 # Verify user is allowed to set this config target = People.by_username(username) if not can_edit_user(identity.current.user, target): flash(_('You cannot edit configs for %s') % username) if request_format() == 'json': return dict(exc='AuthorizationError') else: redirect('/') # Retrieve the data and reformat it as a dict keyed on attribute try: # pylint: disable-msg=E1101 config = Configs.query.filter_by(application=application, attribute=attribute).filter(Configs.person_id==target.id).one() config.value = value except InvalidRequestError: # There was no Config, create a new one config = Configs(application=application, attribute=attribute, value=value) # pylint: disable-msg=E1101 config.person = People.query.filter_by(username=username).one() try: # ScopedSession really does have a flush() method # pylint: disable-msg=E1101 session.flush() except DBAPIError, error: flash(_('Error saving the config to the database: %s' % (error))) return dict(exc='DBAPIError')
def list(self, username, application, pattern=u'*'): '''Retrieve configs for a user and application. Arguments: :username: owner of the configs we're searching for :application: program that the configs are for :pattern: Limit the configs to ones which match this pattern. '*' is a wildcard character Returns: dict that maps the name of the config attribute to the config value. ''' # Only tg controller methods are served via the web so we have to have # methods even if they could be functions. (R0201) # pylint: disable-msg=R0201 # Verify user is allowed to view this config target = People.by_username(username) if not can_edit_user(identity.current.user, target): flash(_('You cannot look at configs for %s') % username) if request_format() == 'json': return dict(exc='AuthorizationError') else: redirect('/') # This only works if pattern is unicode. But it should be unicode # because of the validator. pattern = pattern.translate({ord(u'*'): ur'%'}).lower() # Retrieve the data and reformat it as a dict keyed on attribute # pylint: disable-msg=E1101 cfgs = Configs.query.filter_by(application=application).filter( and_(Configs.attribute.like(pattern), Configs.person_id == target.id)) # pylint: enable-msg=E1101 results = dict((cfg.attribute, cfg.value) for cfg in cfgs.all()) return dict(username=username, application=application, pattern=pattern, configs=results)
def logout(url=None): '''Logout from the server. To use this, replace your current login controller method with:: from fedora.controllers import logout as fc_logout @expose(allow_json=True) def logout(self): return fc_logout() :kwarg url: If provided, when the user logs out, they will always be taken to this url. This defaults to taking the user to the URL they were at when they clicked logout. ''' identity.current.logout() flash(_('You have successfully logged out.')) if request_format() == 'json': return dict() if not url: url = request.headers.get('Referer','/') redirect(url)
def list(self, username, application, pattern=u'*'): '''Retrieve configs for a user and application. Arguments: :username: owner of the configs we're searching for :application: program that the configs are for :pattern: Limit the configs to ones which match this pattern. '*' is a wildcard character Returns: dict that maps the name of the config attribute to the config value. ''' # Only tg controller methods are served via the web so we have to have # methods even if they could be functions. (R0201) # pylint: disable-msg=R0201 # Verify user is allowed to view this config target = People.by_username(username) if not can_edit_user(identity.current.user, target): flash(_('You cannot look at configs for %s') % username) if request_format() == 'json': return dict(exc='AuthorizationError') else: redirect('/') # This only works if pattern is unicode. But it should be unicode # because of the validator. pattern = pattern.translate({ord(u'*'): ur'%'}).lower() # Retrieve the data and reformat it as a dict keyed on attribute # pylint: disable-msg=E1101 cfgs = Configs.query.filter_by(application=application).filter( and_(Configs.attribute.like(pattern), Configs.person_id==target.id)) # pylint: enable-msg=E1101 results = dict((cfg.attribute, cfg.value) for cfg in cfgs.all()) return dict(username=username, application=application, pattern=pattern, configs=results)
def logout(url=None): '''Logout from the server. To use this, replace your current login controller method with:: from fedora.controllers import logout as fc_logout @expose(allow_json=True) def logout(self): return fc_logout() :kwarg url: If provided, when the user logs out, they will always be taken to this url. This defaults to taking the user to the URL they were at when they clicked logout. ''' identity.current.logout() flash(_('You have successfully logged out.')) if request_format() == 'json': return dict() if not url: url = request.headers.get('Referer', '/') redirect(url)
def name(self, packageName, collectionName=None, collectionVersion=None, eol=False): '''Retrieve Packages by their name. This method returns ownership and acl information about a package. When given optional arguments the information can be limited by what collections they are present in. :arg packageName: Name of the package to lookup :kwarg collectionName: If given, limit information to branches for this distribution. :kwarg collectionVersion: If given, limit information to this particular version of a distribution. Has no effect if collectionName is not also specified. :kwarg eol: end-of-life flag. If True, do not limit information. If False, limit information to non-eol collections. ''' #pylint:disable-msg=E1101 # Return the information about a package. package = Package.query.filter( Package.statuscode!=STATUS['Removed'] ).filter_by(name=packageName).first() #pylint:enable-msg=E1101 if not package: error = dict(status=False, title=_('%(app)s -- Invalid Package Name') % { 'app': self.app_title}, message=_('The packagename you were linked to' ' (%(pkg)s) does not appear in the Package Database.' ' If you received this error from a link on the' ' fedoraproject.org website, please report it.') % { 'pkg': packageName}) if request_format() != 'json': error['tg_template'] = 'pkgdb.templates.errors' return error collection = None if collectionName: #pylint:disable-msg=E1101 collection = Collection.query.filter_by(name=collectionName) #pylint:enable-msg=E1101 if collectionVersion: collection = collection.filter_by(version=collectionVersion) if (not eol): collection = collection.filter(Collection.statuscode!=STATUS['EOL']) if not collection.count(): error = dict(status=False, title=_('%(app)s -- Not a Collection') % { 'app': self.app_title}, message=_('%(name)s %(ver)s is not a Collection.') % { 'name': collectionName, 'ver': collectionVersion or ''}) if request_format() != 'json': error['tg_template'] = 'pkgdb.templates.errors' return error # Possible ACLs acl_names = ('watchbugzilla', 'watchcommits', 'commit', 'approveacls') # Possible statuses for acls: acl_status = PackageAclStatus.query.options( #pylint:disable-msg=E1101 eagerload('locale')).all() acl_status_translations = [''] for status in acl_status: ### FIXME: At some point, we have to pull other translations out, # not just C if acl_status_translations != 'Obsolete': acl_status_translations.append( status.locale['C'].statusname) #pylint:disable-msg=E1101 # Fetch information about all the packageListings for this package # The order is a bit complex. We want: # 1) EOL collections last # 2) Within those groups, same named collections together # 3) Within collections, version devel first, # 4) All other collections sorted as numbers in descending order pkg_listings = PackageListing.query.options( eagerload('people2.acls2.status.locale'), eagerload('groups2.acls2.status.locale'), eagerload('status.locale'), eagerload('collection.status.locale'),)\ .filter(PackageListingTable.c.packageid==package.id)\ .join(Collection)\ .order_by(case(value=Collection.statuscode, whens={STATUS['EOL']: 999999}, else_=0), Collection.name, case(value=Collection.version, whens={'devel':999999}, else_=cast(Collection.version, Integer))\ .desc() ) #pylint:enable-msg=E1101 if collection: # User asked to limit it to specific collections pkg_listings = pkg_listings.filter( PackageListingTable.c.collectionid.in_( [c.id for c in collection])) if not pkg_listings.count(): error = dict(status=False, title=_('%(app)s -- Not in Collection') % { 'app': self.app_title}, message=_('The package %(pkg)s is not in Collection' ' %(collctn_name)s %(collctn_ver)s.') % { 'pkg': packageName, 'collctn_name': collectionName, 'collctn_ver': collectionVersion or ''}) if request_format() != 'json': error['tg_template'] = 'pkgdb.templates.errors' return error # Map of statuscode to statusnames used in this package status_map = {} if (not eol): pkg_listings = pkg_listings.filter(Collection.statuscode!=STATUS['EOL']) pkg_listings = pkg_listings.order_by().all() for pkg in pkg_listings: pkg.json_props = {'PackageListing': ('package', 'collection', 'people', 'groups', 'qacontact', 'owner'), 'PersonPackageListing': ('aclOrder', ), 'GroupPackageListing': ('aclOrder', ), } status_map[pkg.statuscode] = pkg.status.locale['C'].statusname status_map[pkg.collection.statuscode] = \ pkg.collection.status.locale['C'].statusname for person in pkg.people: # Setup acls to be accessible via aclName person.aclOrder = {} for acl in acl_names: person.aclOrder[acl] = None for acl in person.acls: statusname = acl.status.locale['C'].statusname status_map[acl.statuscode] = statusname if statusname != 'Obsolete': person.aclOrder[acl.acl] = acl for group in pkg.groups: # Setup acls to be accessible via aclName group.aclOrder = {} for acl in acl_names: group.aclOrder[acl] = None for acl in group.acls: status_map[acl.statuscode] = \ acl.status.locale['C'].statusname group.aclOrder[acl.acl] = acl status_map[pkg_listings[0].package.statuscode] = \ pkg_listings[0].package.status.locale['C'].statusname return dict(title=_('%(title)s -- %(pkg)s') % { 'title': self.app_title, 'pkg': package.name}, packageListings=pkg_listings, statusMap = status_map, aclNames=acl_names, aclStatus=acl_status_translations)
def name(self, packageName, collectionName=None, collectionVersion=None, eol=False): '''Retrieve Packages by their name. This method returns ownership and acl information about a package. When given optional arguments the information can be limited by what collections they are present in. :arg packageName: Name of the package to lookup :kwarg collectionName: If given, limit information to branches for this distribution. :kwarg collectionVersion: If given, limit information to this particular version of a distribution. Has no effect if collectionName is not also specified. :kwarg eol: end-of-life flag. If True, do not limit information. If False, limit information to non-eol collections. ''' #pylint:disable-msg=E1101 # Return the information about a package. package = Package.query.filter( Package.statuscode != STATUS['Removed']).filter_by( name=packageName).first() #pylint:enable-msg=E1101 if not package: error = dict( status=False, title=_('%(app)s -- Invalid Package Name') % {'app': self.app_title}, message=_('The packagename you were linked to' ' (%(pkg)s) does not appear in the Package Database.' ' If you received this error from a link on the' ' fedoraproject.org website, please report it.') % {'pkg': packageName}) if request_format() != 'json': error['tg_template'] = 'pkgdb.templates.errors' return error collection = None if collectionName: #pylint:disable-msg=E1101 collection = Collection.query.filter_by(name=collectionName) #pylint:enable-msg=E1101 if collectionVersion: collection = collection.filter_by(version=collectionVersion) if (not eol): collection = collection.filter( Collection.statuscode != STATUS['EOL']) if not collection.count(): error = dict( status=False, title=_('%(app)s -- Not a Collection') % {'app': self.app_title}, message=_('%(name)s %(ver)s is not a Collection.') % { 'name': collectionName, 'ver': collectionVersion or '' }) if request_format() != 'json': error['tg_template'] = 'pkgdb.templates.errors' return error # Possible ACLs acl_names = ('watchbugzilla', 'watchcommits', 'commit', 'approveacls') # Possible statuses for acls: acl_status = PackageAclStatus.query.options( #pylint:disable-msg=E1101 eagerload('locale')).all() acl_status_translations = [''] for status in acl_status: ### FIXME: At some point, we have to pull other translations out, # not just C if acl_status_translations != 'Obsolete': acl_status_translations.append(status.locale['C'].statusname) #pylint:disable-msg=E1101 # Fetch information about all the packageListings for this package # The order is a bit complex. We want: # 1) EOL collections last # 2) Within those groups, same named collections together # 3) Within collections, version devel first, # 4) All other collections sorted as numbers in descending order pkg_listings = PackageListing.query.options( eagerload('people2.acls2.status.locale'), eagerload('groups2.acls2.status.locale'), eagerload('status.locale'), eagerload('collection.status.locale'),)\ .filter(PackageListingTable.c.packageid==package.id)\ .join(Collection)\ .order_by(case(value=Collection.statuscode, whens={STATUS['EOL']: 999999}, else_=0), Collection.name, case(value=Collection.version, whens={'devel':999999}, else_=cast(Collection.version, Integer))\ .desc() ) #pylint:enable-msg=E1101 if collection: # User asked to limit it to specific collections pkg_listings = pkg_listings.filter( PackageListingTable.c.collectionid.in_( [c.id for c in collection])) if not pkg_listings.count(): error = dict( status=False, title=_('%(app)s -- Not in Collection') % {'app': self.app_title}, message=_('The package %(pkg)s is not in Collection' ' %(collctn_name)s %(collctn_ver)s.') % { 'pkg': packageName, 'collctn_name': collectionName, 'collctn_ver': collectionVersion or '' }) if request_format() != 'json': error['tg_template'] = 'pkgdb.templates.errors' return error # Map of statuscode to statusnames used in this package status_map = {} if (not eol): pkg_listings = pkg_listings.filter( Collection.statuscode != STATUS['EOL']) pkg_listings = pkg_listings.order_by().all() for pkg in pkg_listings: pkg.json_props = { 'PackageListing': ('package', 'collection', 'people', 'groups', 'qacontact', 'owner'), 'PersonPackageListing': ('aclOrder', ), 'GroupPackageListing': ('aclOrder', ), } status_map[pkg.statuscode] = pkg.status.locale['C'].statusname status_map[pkg.collection.statuscode] = \ pkg.collection.status.locale['C'].statusname for person in pkg.people: # Setup acls to be accessible via aclName person.aclOrder = {} for acl in acl_names: person.aclOrder[acl] = None for acl in person.acls: statusname = acl.status.locale['C'].statusname status_map[acl.statuscode] = statusname if statusname != 'Obsolete': person.aclOrder[acl.acl] = acl for group in pkg.groups: # Setup acls to be accessible via aclName group.aclOrder = {} for acl in acl_names: group.aclOrder[acl] = None for acl in group.acls: status_map[acl.statuscode] = \ acl.status.locale['C'].statusname group.aclOrder[acl.acl] = acl status_map[pkg_listings[0].package.statuscode] = \ pkg_listings[0].package.status.locale['C'].statusname return dict(title=_('%(title)s -- %(pkg)s') % { 'title': self.app_title, 'pkg': package.name }, packageListings=pkg_listings, statusMap=status_map, aclNames=acl_names, aclStatus=acl_status_translations)
def packages(self, fasname=None, acls=None, eol=None): '''List packages that the user is interested in. This method returns a list of packages owned by the user in current, non-EOL distributions. The user has the ability to filter this to provide more or less information by adding query params for acls and EOL. :kwarg fasname: The name of the user to get the package list for. Default: The logged in user. :kwarg acls: List of acls to select. Note: for backwards compatibility, this can also be a comma separated string of acls. Default: all acls. :kwarg eol: If set, list packages that are in EOL distros. :returns: A list of packages. ''' # Set EOL to false for a few obvious values if not eol or eol.lower() in ('false', 'f', '0'): eol = False else: eol = bool(eol) # For backward compat, redirect the orphan user to the orphan page if fasname == 'orphan': params = {} if eol: params['eol'] = True if request_format() == 'json': params['tg_format'] = 'json' url = '/acls/orphans' if params: url = url + '?' + urllib.urlencode(params, True) raise redirect(url) if not acls: # Default to all acls acls = [k[0] for k in self.allAcls] elif isinstance(acls, basestring): # For backwards compatibility, make acls into a list if it's a # comma separated string of values acls = acls.split(',') # Create a list where store acl name, whether the acl is currently # being filtered for, and the label to use to display the acl. acl_list = [(a[0], a[0] in acls, a[1]) for a in self.allAcls] # Have to either get fasname from the URL or current user if fasname == None: if identity.current.anonymous: raise identity.IdentityFailure( _('You must be logged in to view your information')) else: fasname = identity.current.user_name page_title = _('%(app)s -- %(name)s -- Packages') % { 'app': self.app_title, 'name': fasname} # pylint: disable-msg=E1101 query = Package.query.join('listings2').distinct().options( lazyload('listings2.groups2'), lazyload('listings2.groups2.acls2'), lazyload('listings2.people2'), lazyload('listings2.people2.acls2'), lazyload('listings2')) if not eol: # We don't want EOL releases, filter those out of each clause query = query.join(['listings2', 'collection']).filter( Collection.statuscode != STATUS['EOL']) queries = [] if 'owner' in acls: # Return any package for which the user is the owner queries.append(query.filter(sqlalchemy.and_( Package.statuscode.in_(( STATUS['Approved'], STATUS['Awaiting Review'], STATUS['Under Review'])), PackageListing.owner==fasname, PackageListing.statuscode.in_(( STATUS['Approved'], STATUS['Awaiting Branch'], STATUS['Awaiting Review'])) ))) del acls[acls.index('owner')] if acls: # Return any package on which the user has an Approved acl. queries.append(query.join(['listings2', 'people2']).join( ['listings2', 'people2', 'acls2']).filter(sqlalchemy.and_( Package.statuscode.in_((STATUS['Approved'], STATUS['Awaiting Review'], STATUS['Under Review'])), PersonPackageListing.username == fasname, PersonPackageListingAcl.statuscode == STATUS['Approved'], PackageListing.statuscode.in_((STATUS['Approved'], STATUS['Awaiting Branch'], STATUS['Awaiting Review'])) ))) # Return only those acls which the user wants listed queries[-1] = queries[-1].filter( PersonPackageListingAcl.acl.in_(acls)) if len(queries) == 2: my_pkgs = Package.query.select_from( sqlalchemy.union( queries[0].statement, queries[1].statement )) else: my_pkgs = queries[0] my_pkgs = my_pkgs.options(lazyload('listings2.people2'), lazyload('listings2.people2.acls2'), lazyload('listings2.groups2'), lazyload('listings2.groups2.acls2'), lazyload('listings2') ).order_by(Package.name) # pylint: enable-msg=E1101 pkg_list = [] statuses = set() collectn_map = {} for pkg in my_pkgs: pkg.json_props = {'Package': ('listings',)} pkg_list.append(pkg) for pkglisting in pkg.listings: statuses.add(pkglisting.statuscode) if pkglisting.collection.collectionid not in collectn_map \ and pkglisting.collection.statuscode!=STATUS['EOL']: collectn_map[pkglisting.collection.collectionid] = pkglisting.collection.branchname statusMap = dict([(statuscode, STATUS[statuscode]) for statuscode in statuses]) return dict(title=page_title, pkgCount=len(pkg_list), pkgs=pkg_list, acls=acl_list, fasname=fasname, eol=eol, statusMap=statusMap, collectn_map=collectn_map)
def default(self, package_name, *args, **kwargs): '''Display a list of Fedora bugs against a given package.''' # Nasty, nasty hack. The packagedb, via bugz.fp.o is getting sent # requests to download files. These refused to go away even when # we fixed up the apache redirects. Send them to download.fp.o # manually. if args or kwargs: if args: url = 'http://download.fedoraproject.org/' \ + quote(package_name) \ + '/' + '/'.join([quote(a) for a in args]) elif kwargs: url = 'http://mirrors.fedoraproject.org/' \ + quote(package_name) \ + '?' + '&'.join([quote(q) + '=' + quote(v) for (q, v) in kwargs.items()]) LOG.warning( _('Invalid URL: redirecting: %(url)s') % {'url': url}) raise redirect(url) query = { 'product': ('Fedora', 'Fedora EPEL'), 'component': package_name, 'bug_status': ('ASSIGNED', 'NEW', 'MODIFIED', 'ON_DEV', 'ON_QA', 'VERIFIED', 'FAILS_QA', 'RELEASE_PENDING', 'POST') } # :E1101: python-bugzilla monkey patches this in try: bugzilla = get_bz() except xmlrpclib.ProtocolError: error = dict( status=False, title=_('%(app)s -- Unable to contact bugzilla') % {'app': self.app_title}, message=_('Bugzilla is unavailable. Unable to determine' ' bugs for %(pkg)s') % {'pkg': package_name}) if request_format() != 'json': error['tg_template'] = 'pkgdb.templates.errors' return error raw_bugs = bugzilla.query(query) # pylint: disable-msg=E1101 bugs = BugList(self.bzQueryUrl, self.bzUrl) for bug in raw_bugs: bugs.append(bug) if not bugs: # Check that the package exists try: # pylint: disable-msg=E1101 Package.query.filter_by(name=package_name).one() except InvalidRequestError: error = dict(status=False, title=_('%(app)s -- Not a Valid Package Name') % {'app': self.app_title}, message=_('No such package %(pkg)s') % {'pkg': package_name}) if request_format() != 'json': error['tg_template'] = 'pkgdb.templates.errors' return error return dict(title=_('%(app)s -- Open Bugs for %(pkg)s') % { 'app': self.app_title, 'pkg': package_name }, package=package_name, bugs=bugs)
class FPCA(controllers.Controller): ''' Processes FPCA workflow ''' # Group name for people having signed the FPCA CLAGROUPNAME = config.get('cla_standard_group') # Meta group for everyone who has satisfied the requirements of the FPCA # (By signing or having a corporate signatue or, etc) CLAMETAGROUPNAME = config.get('cla_done_group') # Values legal in phone numbers PHONEDIGITS = ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '+', '-', ')' ,'(', ' ') def __init__(self): '''Create a FPCA Controller.''' @identity.require(turbogears.identity.not_anonymous()) @expose(template="fas.templates.fpca.index") def index(self): '''Display the FPCAs (and accept/do not accept buttons)''' show = {} show['show_postal_address'] = config.get('show_postal_address') username = turbogears.identity.current.user_name person = People.by_username(username) try: code_len = len(person.country_code) except TypeError: code_len = 0 if show['show_postal_address']: contactInfo = person.telephone or person.postal_address if person.country_code == 'O1' and not person.telephone: turbogears.flash(_('A telephone number is required to ' + \ 'complete the FPCA. Please fill out below.')) elif not person.country_code or not person.human_name \ or not contactInfo: turbogears.flash(_('A valid country and telephone number ' + \ 'or postal address is required to complete the FPCA. ' + \ 'Please fill them out below.')) else: if not person.telephone or code_len != 2 or \ person.country_code == ' ': turbogears.flash(_('A valid country and telephone number are' + ' required to complete the FPCA. Please fill them ' + 'out below.')) (cla, undeprecated_cla) = undeprecated_cla_done(person) person = person.filter_private() return dict(cla=undeprecated_cla, person=person, date=datetime.utcnow().ctime(), show=show) def _cla_dependent(self, group): ''' Check whether a group has the cla in its prerequisite chain. Arguments: :group: group to check Returns: True if the group requires the cla_group_name otherwise ''' if group.name in (self.CLAGROUPNAME, self.CLAMETAGROUPNAME): return True if group.prerequisite_id: return self._cla_dependent(group.prerequisite) return False def json_request(self): ''' Helps define if json is being used for this request :returns: 1 or 0 depending on if request is json or not ''' return 'tg_format' in cherrypy.request.params and \ cherrypy.request.params['tg_format'] == 'json' @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()) @expose(template = "genshi-text:fas.templates.fpca.fpca", format = "text", content_type = 'text/plain; charset=utf-8') def text(self): '''View FPCA as text''' username = turbogears.identity.current.user_name person = People.by_username(username) person = person.filter_private() return dict(person=person, date=datetime.utcnow().ctime()) @identity.require(turbogears.identity.not_anonymous()) @expose(template = "genshi-text:fas.templates.fpca.fpca", format = "text", content_type = 'text/plain; charset=utf-8') def download(self): '''Download FPCA''' username = turbogears.identity.current.user_name person = People.by_username(username) person = person.filter_private() return dict(person=person, date=datetime.utcnow().ctime()) @identity.require(turbogears.identity.not_anonymous()) @expose(template="fas.templates.user.view", allow_json=True) def reject(self, person_name): '''Reject a user's FPCA. This method will remove a user from the FPCA group and any other groups that they are in that require the FPCA. It is used when a person has to fulfill some more legal requirements before having a valid FPCA. Arguments :person_name: Name of the person to reject. ''' show = {} show['show_postal_address'] = config.get('show_postal_address') exc = None user = People.by_username(turbogears.identity.current.user_name) if not is_admin(user): # Only admins can use this turbogears.flash(_('You are not allowed to reject FPCAs.')) exc = 'NotAuthorized' else: # Unapprove the cla and all dependent groups person = People.by_username(person_name) for role in person.roles: if self._cla_dependent(role.group): role.role_status = 'unapproved' try: session.flush() except DBAPIError, error: turbogears.flash(_('Error removing cla and dependent groups' \ ' for %(person)s\n Error was: %(error)s') % {'person': person_name, 'error': str(error)}) exc = 'DBAPIError' if not exc: # Send a message that the ICLA has been revoked date_time = datetime.utcnow() Log(author_id=user.id, description='Revoked %s FPCA' % person.username, changetime=date_time) revoke_subject = _('Fedora ICLA Revoked', person.locale) i18n_revoke_text = _(''' Hello %(human_name)s, We're sorry to bother you but we had to reject your FPCA for now because information you provided has been deemed incorrect. The most common cause of this is people abbreviating their name like "B L Couper" instead of providing their actual full name "Bill Lay Couper". Other causes of this include are using a country, or phone number that isn't accurate [1]_. If you could edit your account [2]_ to fix any of these problems and resubmit the FPCA we would appreciate it. .. [1]: Why does it matter that we have your real name and phone number? It's because the FPCA is a legal document and should we ever need to contact you about one of your contributions (as an example, because someone contacts *us* claiming that it was really they who own the copyright to the contribution) we might need to contact you for more information about what's going on. .. [2]: Edit your account by logging in at this URL: https://admin.fedoraproject.org/accounts/user/edit/%(username)s If you have questions about what specifically might be the problem with your account, please contact us at [email protected]. Thanks! ''' % {'username': person.username, 'human_name': person.human_name}, person.locale) #TODO: Look at a better way to handle one text for mutiple usage. # while dealing with pot files. std_revoke_text = ''' English version: Hello %(human_name)s, We're sorry to bother you but we had to reject your FPCA for now because information you provided has been deemed incorrect. The most common cause of this is people abbreviating their name like "B L Couper" instead of providing their actual full name "Bill Lay Couper". Other causes of this include are using a country, or phone number that isn't accurate [1]_. If you could edit your account [2]_ to fix any of these problems and resubmit the FPCA we would appreciate it. .. [1]: Why does it matter that we have your real name and phone number? It's because the FPCA is a legal document and should we ever need to contact you about one of your contributions (as an example, because someone contacts *us* claiming that it was really they who own the copyright to the contribution) we might need to contact you for more information about what's going on. .. [2]: Edit your account by logging in at this URL: https://admin.fedoraproject.org/accounts/user/edit/%(username)s If you have questions about what specifically might be the problem with your account, please contact us at [email protected]. Thanks! ''' % {'username': person.username, 'human_name': person.human_name} revoke_text = i18n_revoke_text + std_revoke_text send_mail(person.email, revoke_subject, revoke_text) # Yay, sweet success! turbogears.flash(_('FPCA Successfully Removed.')) # and now we're done if request_format() == 'json': return_val = {} if exc: return_val['exc'] = exc return return_val else: turbogears.redirect('/user/view/%s' % person_name)
class Root(plugin.RootController): user = User() group = Group() fpca = FPCA() json = JsonRequest() config = Config() help = Help() def __init__(self): # TODO: Find a better place for this. os.environ['GNUPGHOME'] = config.get('gpghome') plugin.RootController.__init__(self) def getpluginident(self): return 'fas' @expose(template="fas.templates.welcome", allow_json=True) def index(self): if turbogears.identity.not_anonymous(): if request_format() == 'json': # redirects don't work with JSON calls. This is a bit of a # hack until we can figure out something better. return dict() turbogears.redirect('/home') return dict(now=time.ctime()) @identity.require(identity.not_anonymous()) @expose(template="fas.templates.home", allow_json=True) def home(self): user_name = turbogears.identity.current.user_name person = People.by_username(user_name) (cla_done, undeprecated_cla) = undeprecated_cla_done(person) person = person.filter_private() return dict(person=person, memberships=person['memberships'], cla=undeprecated_cla) @expose(template="fas.templates.about") def about(self): return dict() @expose(template="fas.templates.login", allow_json=True) def login(self, forward_url=None, *args, **kwargs): '''Page to become authenticated to the Account System. This shows a small login box to type in your username and password from the Fedora Account System. :kwarg forward_url: The url to send to once authentication succeeds ''' actual_login_dict = f_ctrlers.login(forward_url=forward_url, *args, **kwargs) try: login_dict = Bunch() login_dict['user'] = Bunch() for field in People.allow_fields['complete']: login_dict['user'][field] = None for field in People.allow_fields['self']: login_dict['user'][field] = getattr(actual_login_dict['user'], field) # Strip out things that the user shouldn't see about their own # login login_dict['user']['internal_comments'] = None login_dict['user']['emailtoken'] = None login_dict['user']['security_answer'] = None login_dict['user']['alias_enabled'] = None login_dict['user']['passwordtoken'] = None # Add things that are needed by some other apps login_dict['user'].approved_memberships = list( actual_login_dict['user'].approved_memberships) login_dict['user'].memberships = list( actual_login_dict['user'].memberships) login_dict['user'].unapproved_memberships = list( actual_login_dict['user'].unapproved_memberships) login_dict['user'].group_roles = list( actual_login_dict['user'].group_roles) login_dict['user'].roles = list(actual_login_dict['user'].roles) login_dict['user'].groups = [ g.name for g in actual_login_dict['user'].approved_memberships ] return login_dict except KeyError, e: # No problem, this usually means that we failed to login and # therefore we don't have a user field. login_dict = actual_login_dict if not identity.current.anonymous and identity.was_login_attempted() \ and not identity.get_identity_errors(): # Success that needs to be passed back via json return login_dict if identity.was_login_attempted() and request.fas_provided_username: if request.fas_identity_failure_reason == 'status_inactive': turbogears.flash( _('Your old password has expired. Please' ' reset your password below.')) if request_format() != 'json': redirect('/user/resetpass') if request.fas_identity_failure_reason == 'status_account_disabled': turbogears.flash( _('Your account is currently disabled. For' ' more information, please contact %(admin_email)s' % {'admin_email': config.get('accounts_email')})) if request_format() != 'json': redirect('/login') return login_dict
def default(self, package_name, *args, **kwargs): """Display a list of Fedora bugs against a given package.""" # Nasty, nasty hack. The packagedb, via bugz.fp.o is getting sent # requests to download files. These refused to go away even when # we fixed up the apache redirects. Send them to download.fp.o # manually. if args or kwargs: if args: url = ( "http://download.fedoraproject.org/" + quote(package_name) + "/" + "/".join([quote(a) for a in args]) ) elif kwargs: url = ( "http://mirrors.fedoraproject.org/" + quote(package_name) + "?" + "&".join([quote(q) + "=" + quote(v) for (q, v) in kwargs.items()]) ) LOG.warning(_("Invalid URL: redirecting: %(url)s") % {"url": url}) raise redirect(url) query = { "product": ("Fedora", "Fedora EPEL"), "component": package_name, "bug_status": ( "ASSIGNED", "NEW", "MODIFIED", "ON_DEV", "ON_QA", "VERIFIED", "FAILS_QA", "RELEASE_PENDING", "POST", ), } # :E1101: python-bugzilla monkey patches this in try: bugzilla = get_bz() except xmlrpclib.ProtocolError: error = dict( status=False, title=_("%(app)s -- Unable to contact bugzilla") % {"app": self.app_title}, message=_("Bugzilla is unavailable. Unable to determine" " bugs for %(pkg)s") % {"pkg": package_name}, ) if request_format() != "json": error["tg_template"] = "pkgdb.templates.errors" return error raw_bugs = bugzilla.query(query) # pylint: disable-msg=E1101 bugs = BugList(self.bzQueryUrl, self.bzUrl) for bug in raw_bugs: bugs.append(bug) if not bugs: # Check that the package exists try: # pylint: disable-msg=E1101 Package.query.filter_by(name=package_name).one() except InvalidRequestError: error = dict( status=False, title=_("%(app)s -- Not a Valid Package Name") % {"app": self.app_title}, message=_("No such package %(pkg)s") % {"pkg": package_name}, ) if request_format() != "json": error["tg_template"] = "pkgdb.templates.errors" return error return dict( title=_("%(app)s -- Open Bugs for %(pkg)s") % {"app": self.app_title, "pkg": package_name}, package=package_name, bugs=bugs, )
def packages(self, fasname=None, acls=None, eol=None): '''List packages that the user is interested in. This method returns a list of packages owned by the user in current, non-EOL distributions. The user has the ability to filter this to provide more or less information by adding query params for acls and EOL. :kwarg fasname: The name of the user to get the package list for. Default: The logged in user. :kwarg acls: List of acls to select. Note: for backwards compatibility, this can also be a comma separated string of acls. Default: all acls. :kwarg eol: If set, list packages that are in EOL distros. :returns: A list of packages. ''' # Set EOL to false for a few obvious values if not eol or eol.lower() in ('false', 'f', '0'): eol = False else: eol = bool(eol) # For backward compat, redirect the orphan user to the orphan page if fasname == 'orphan': params = {} if eol: params['eol'] = True if request_format() == 'json': params['tg_format'] = 'json' url = '/acls/orphans' if params: url = url + '?' + urllib.urlencode(params, True) raise redirect(url) if not acls: # Default to all acls acls = [k[0] for k in self.allAcls] elif isinstance(acls, basestring): # For backwards compatibility, make acls into a list if it's a # comma separated string of values acls = acls.split(',') # Create a list where store acl name, whether the acl is currently # being filtered for, and the label to use to display the acl. acl_list = [(a[0], a[0] in acls, a[1]) for a in self.allAcls] # Have to either get fasname from the URL or current user if fasname == None: if identity.current.anonymous: raise identity.IdentityFailure( _('You must be logged in to view your information')) else: fasname = identity.current.user_name page_title = _('%(app)s -- %(name)s -- Packages') % { 'app': self.app_title, 'name': fasname } # pylint: disable-msg=E1101 query = Package.query.join('listings2').distinct().options( lazyload('listings2.groups2'), lazyload('listings2.groups2.acls2'), lazyload('listings2.people2'), lazyload('listings2.people2.acls2'), lazyload('listings2')) if not eol: # We don't want EOL releases, filter those out of each clause query = query.join( ['listings2', 'collection']).filter(Collection.statuscode != STATUS['EOL']) queries = [] if 'owner' in acls: # Return any package for which the user is the owner queries.append( query.filter( sqlalchemy.and_( Package.statuscode.in_( (STATUS['Approved'], STATUS['Awaiting Review'], STATUS['Under Review'])), PackageListing.owner == fasname, PackageListing.statuscode.in_( (STATUS['Approved'], STATUS['Awaiting Branch'], STATUS['Awaiting Review']))))) del acls[acls.index('owner')] if acls: # Return any package on which the user has an Approved acl. queries.append( query.join(['listings2', 'people2']).join( ['listings2', 'people2', 'acls2']).filter( sqlalchemy.and_( Package.statuscode.in_( (STATUS['Approved'], STATUS['Awaiting Review'], STATUS['Under Review'])), PersonPackageListing.username == fasname, PersonPackageListingAcl.statuscode == STATUS['Approved'], PackageListing.statuscode.in_( (STATUS['Approved'], STATUS['Awaiting Branch'], STATUS['Awaiting Review']))))) # Return only those acls which the user wants listed queries[-1] = queries[-1].filter( PersonPackageListingAcl.acl.in_(acls)) if len(queries) == 2: my_pkgs = Package.query.select_from( sqlalchemy.union(queries[0].statement, queries[1].statement)) else: my_pkgs = queries[0] my_pkgs = my_pkgs.options(lazyload('listings2.people2'), lazyload('listings2.people2.acls2'), lazyload('listings2.groups2'), lazyload('listings2.groups2.acls2'), lazyload('listings2')).order_by(Package.name) # pylint: enable-msg=E1101 pkg_list = [] statuses = set() collectn_map = {} for pkg in my_pkgs: pkg.json_props = {'Package': ('listings', )} pkg_list.append(pkg) for pkglisting in pkg.listings: statuses.add(pkglisting.statuscode) if pkglisting.collection.collectionid not in collectn_map \ and pkglisting.collection.statuscode!=STATUS['EOL']: collectn_map[ pkglisting.collection. collectionid] = pkglisting.collection.branchname statusMap = dict([(statuscode, STATUS[statuscode]) for statuscode in statuses]) return dict(title=page_title, pkgCount=len(pkg_list), pkgs=pkg_list, acls=acl_list, fasname=fasname, eol=eol, statusMap=statusMap, collectn_map=collectn_map)