def redirect_branch_or_repo(self): """Redirect /+code/<foo> to the branch or repository named 'foo'. 'foo' can be the unique name of the branch/repo, or any of the aliases for the branch/repo. If 'foo' is invalid, or exists but the user does not have permission to view 'foo', a NotFoundError is raised, resulting in a 404 error. Unlike +branch, this works for both git and bzr repositories/branches. """ target_url = None path = '/'.join(self.request.stepstogo) # Try a Git repository lookup first, since the schema is simpler and # so it's quicker. try: repository, trailing = getUtility(IGitLookup).getByPath(path) if repository is not None: target_url = canonical_url(repository) if trailing: target_url = urlappend(target_url, trailing) except (InvalidNamespace, InvalidProductName, NameLookupFailed, Unauthorized): # Either the git repository wasn't found, or it was found but we # lack authority to access it. In either case, attempt a bzr lookup # so don't set target_url pass # Attempt a bzr lookup as well: try: branch, trailing = getUtility(IBranchLookup).getByLPPath(path) bzr_url = canonical_url(branch) if trailing != '': bzr_url = urlappend(bzr_url, trailing) if target_url and branch.product is not None: # Project has both a bzr branch and a git repo. There's no # right thing we can do here, so pretend we didn't see # anything at all. if branch.product.vcs is None: target_url = None # if it's set to BZR, then set this branch as the target if branch.product.vcs == VCSType.BZR: target_url = bzr_url else: target_url = bzr_url except (NoLinkedBranch, CannotHaveLinkedBranch, InvalidNamespace, InvalidProductName, NotFoundError): # No bzr branch found either. pass # Either neither bzr nor git returned matches, or they did but we're # not authorised to view them, or they both did and the project has not # set its 'vcs' property to indicate which one to prefer. In all cases # raise a 404: if not target_url: raise NotFoundError return self.redirectSubTree(target_url)
def getExternalBugTrackerToUse(self): """See `IExternalBugTracker`.""" base_auth_url = urlappend(self.baseurl, 'launchpad-auth') # Any token will do. auth_url = urlappend(base_auth_url, 'check') try: response = self.urlopen(auth_url) except urllib2.HTTPError as error: # If the error is HTTP 401 Unauthorized then we're # probably talking to the LP plugin. if error.code == 401: return TracLPPlugin(self.baseurl) else: return self except urllib2.URLError as error: return self else: # If the response contains a trac_auth cookie then we're # talking to the LP plugin. However, it's unlikely that # the remote system will authorize the bogus auth token we # sent, so this check is really intended to detect broken # Trac instances that return HTTP 200 for a missing page. for set_cookie in response.headers.getheaders('Set-Cookie'): cookie = SimpleCookie(set_cookie) if 'trac_auth' in cookie: return TracLPPlugin(self.baseurl) else: return self
def getExternalBugTrackerToUse(self): """See `IExternalBugTracker`.""" base_auth_url = urlappend(self.baseurl, 'launchpad-auth') # Any token will do. auth_url = urlappend(base_auth_url, 'check') try: with override_timeout(config.checkwatches.default_socket_timeout): response = urlfetch(auth_url, use_proxy=True) except requests.HTTPError as e: # If the error is HTTP 401 Unauthorized then we're # probably talking to the LP plugin. if e.response.status_code == 401: return TracLPPlugin(self.baseurl) else: return self except requests.RequestException: return self else: # If the response contains a trac_auth cookie then we're # talking to the LP plugin. However, it's unlikely that # the remote system will authorize the bogus auth token we # sent, so this check is really intended to detect broken # Trac instances that return HTTP 200 for a missing page. for cookie in response.cookies: if cookie.name == 'trac_auth': return TracLPPlugin(self.baseurl) else: return self
def getExternalBugTrackerToUse(self): """See `IExternalBugTracker`.""" base_auth_url = urlappend(self.baseurl, 'launchpad-auth') # Any token will do. auth_url = urlappend(base_auth_url, 'check') try: response = self.urlopen(auth_url) except urllib2.HTTPError as error: # If the error is HTTP 401 Unauthorized then we're # probably talking to the LP plugin. if error.code == 401: return TracLPPlugin(self.baseurl) else: return self except urllib2.URLError as error: return self else: # If the response contains a trac_auth cookie then we're # talking to the LP plugin. However, it's unlikely that # the remote system will authorize the bogus auth token we # sent, so this check is really intended to detect broken # Trac instances that return HTTP 200 for a missing page. for set_cookie in response.headers.getheaders('Set-Cookie'): cookie = SimpleCookie(set_cookie) if 'trac_auth' in cookie: return TracLPPlugin(self.baseurl) else: return self
def _authenticate(self): """Authenticate with the Trac instance.""" token_text = self._generateAuthenticationToken() base_auth_url = urlappend(self.baseurl, 'launchpad-auth') auth_url = urlappend(base_auth_url, token_text) try: self._fetchPage(auth_url) except BugTrackerConnectError as e: raise BugTrackerAuthenticationError(self.baseurl, e.error)
def _authenticate(self): """Authenticate with the Trac instance.""" token_text = self._generateAuthenticationToken() base_auth_url = urlappend(self.baseurl, 'launchpad-auth') auth_url = urlappend(base_auth_url, token_text) try: self._getPage(auth_url) except BugTrackerConnectError as e: raise BugTrackerAuthenticationError(self.baseurl, e.error)
def traverse(self, path, first_segment, use_default_referer=True): """Traverse to 'path' using a 'LaunchpadRootNavigation' object. Using the Zope traversal machinery, traverse to the path given by 'segments', starting at a `LaunchpadRootNavigation` object. CAUTION: Prefer test_traverse to this method, because it correctly establishes the global request. :param path: A slash-delimited path. :param use_default_referer: If True, set the referer attribute in the request header to DEFAULT_REFERER = "http://launchpad.dev" (otherwise it remains as None) :return: The object found. """ # XXX: What's the difference between first_segment and path? -- mbp # 2011-06-27. extra = {'PATH_INFO': urlappend('/%s' % first_segment, path)} if use_default_referer: extra['HTTP_REFERER'] = DEFAULT_REFERER request = LaunchpadTestRequest(**extra) segments = reversed(path.split('/')) request.setTraversalStack(segments) traverser = LaunchpadRootNavigation( getUtility(ILaunchpadRoot), request=request) return traverser.publishTraverse(request, first_segment)
def href(self): """The location of the feed. E.g. http://feeds.launchpad.net/firefox/revisions.atom """ return urlappend(canonical_url(self.context, rootsite='feeds'), 'revisions.atom')
def traverse(self, path, first_segment, use_default_referer=True): """Traverse to 'path' using a 'LaunchpadRootNavigation' object. Using the Zope traversal machinery, traverse to the path given by 'segments', starting at a `LaunchpadRootNavigation` object. CAUTION: Prefer test_traverse to this method, because it correctly establishes the global request. :param path: A slash-delimited path. :param use_default_referer: If True, set the referer attribute in the request header to DEFAULT_REFERER = "http://launchpad.dev" (otherwise it remains as None) :return: The object found. """ # XXX: What's the difference between first_segment and path? -- mbp # 2011-06-27. extra = {'PATH_INFO': urlappend('/%s' % first_segment, path)} if use_default_referer: extra['HTTP_REFERER'] = DEFAULT_REFERER request = LaunchpadTestRequest(**extra) segments = reversed(path.split('/')) request.setTraversalStack(segments) traverser = LaunchpadRootNavigation(getUtility(ILaunchpadRoot), request=request) return traverser.publishTraverse(request, first_segment)
def get_server_url(): """Return the URL for this server's OpenID endpoint. This is wrapped in a function (instead of a constant) to make sure the vhost.testopenid section is not required in production configs. """ return urlappend(allvhosts.configs['testopenid'].rooturl, '+openid')
def _uploadFile(cls, lfa, lfc): """Upload a single file.""" assert config.snappy.store_upload_url is not None unscanned_upload_url = urlappend( config.snappy.store_upload_url, "unscanned-upload/") lfa.open() try: lfa_wrapper = LibraryFileAliasWrapper(lfa) encoder = MultipartEncoder( fields={ "binary": ( lfa.filename, lfa_wrapper, "application/octet-stream"), }) # XXX cjwatson 2016-05-09: This should add timeline information, # but that's currently difficult in jobs. try: response = urlfetch( unscanned_upload_url, method="POST", data=encoder, headers={ "Content-Type": encoder.content_type, "Accept": "application/json", }) response_data = response.json() if not response_data.get("successful", False): raise UploadFailedResponse(response.text) return {"upload_id": response_data["upload_id"]} except requests.HTTPError as e: raise cls._makeSnapStoreError(UploadFailedResponse, e) finally: lfa.close()
def getRedirectURL(self, current_url, query_string): """Get the URL to redirect to. :param current_url: The URL of the current page. :param query_string: The string that should be appended to the current url. """ return urlappend(current_url, '+login' + query_string)
def getRedirectURL(self, current_url, query_string): """Get the URL to redirect to. :param current_url: The URL of the current page. :param query_string: The string that should be appended to the current url. """ return urlappend(current_url, '+login' + query_string)
def _uploadApp(cls, snap, upload_data): """Create a new store upload based on the uploaded file.""" assert config.snappy.store_url is not None assert snap.store_name is not None upload_url = urlappend(config.snappy.store_url, "dev/api/snap-push/") data = { "name": snap.store_name, "updown_id": upload_data["upload_id"], "series": snap.store_series.name, } # XXX cjwatson 2016-05-09: This should add timeline information, but # that's currently difficult in jobs. try: assert snap.store_secrets is not None response = urlfetch( upload_url, method="POST", json=data, auth=MacaroonAuth( snap.store_secrets["root"], snap.store_secrets.get("discharge"))) response_data = response.json() return response_data["status_details_url"] except requests.HTTPError as e: if e.response.status_code == 401: if (e.response.headers.get("WWW-Authenticate") == "Macaroon needs_refresh=1"): raise NeedsRefreshResponse() else: raise cls._makeSnapStoreError( UnauthorizedUploadResponse, e) raise cls._makeSnapStoreError(UploadFailedResponse, e)
def href(self): """The location of the feed. E.g. http://feeds.launchpad.net/firefox/revisions.atom """ return urlappend(canonical_url(self.context, rootsite='feeds'), 'revisions.atom')
def get_server_url(): """Return the URL for this server's OpenID endpoint. This is wrapped in a function (instead of a constant) to make sure the vhost.testopenid section is not required in production configs. """ return urlappend(allvhosts.configs['testopenid'].rooturl, '+openid')
def _release(cls, snap, revision): """Release a snap revision to specified channels.""" release_url = urlappend( config.snappy.store_url, "dev/api/snap-release/") data = { "name": snap.store_name, "revision": revision, # The security proxy is useless and breaks JSON serialisation. "channels": removeSecurityProxy(snap.store_channels), "series": snap.store_series.name, } # XXX cjwatson 2016-06-28: This should add timeline information, but # that's currently difficult in jobs. try: assert snap.store_secrets is not None urlfetch( release_url, method="POST", json=data, auth=MacaroonAuth( snap.store_secrets["root"], snap.store_secrets.get("discharge"))) except requests.HTTPError as e: if e.response.status_code == 401: if (e.response.headers.get("WWW-Authenticate") == "Macaroon needs_refresh=1"): raise NeedsRefreshResponse() raise cls._makeSnapStoreError(ReleaseFailedResponse, e)
def describeJobs(self, dsd): """Describe any jobs that may be pending for `dsd`. Shows "synchronizing..." if the entry is being synchronized, "updating..." if the DSD is being updated with package changes and "waiting in <queue>..." if the package is in the distroseries queues (<queue> will be NEW or UNAPPROVED and links to the relevant queue page). :param dsd: A `DistroSeriesDifference` on the page. :return: An HTML text describing work that is pending or in progress for `dsd`; or None. """ has_pending_dsd_update = self.hasPendingDSDUpdate(dsd) pending_sync = self.pendingSync(dsd) if not has_pending_dsd_update and not pending_sync: return None description = [] if has_pending_dsd_update: description.append("updating") if pending_sync is not None: # If the pending sync is waiting in the distroseries queues, # provide a handy link to there. queue_item = getUtility(IPackageUploadSet).getByPackageCopyJobIDs( (pending_sync.id, )).any() if queue_item is None: description.append("synchronizing") else: url = urlappend( canonical_url(self.context), "+queue?queue_state=%s" % queue_item.status.value) description.append('waiting in <a href="%s">%s</a>' % (url, queue_item.status.name)) return " and ".join(description) + "…"
def describeJobs(self, dsd): """Describe any jobs that may be pending for `dsd`. Shows "synchronizing..." if the entry is being synchronized, "updating..." if the DSD is being updated with package changes and "waiting in <queue>..." if the package is in the distroseries queues (<queue> will be NEW or UNAPPROVED and links to the relevant queue page). :param dsd: A `DistroSeriesDifference` on the page. :return: An HTML text describing work that is pending or in progress for `dsd`; or None. """ has_pending_dsd_update = self.hasPendingDSDUpdate(dsd) pending_sync = self.pendingSync(dsd) if not has_pending_dsd_update and not pending_sync: return None description = [] if has_pending_dsd_update: description.append("updating") if pending_sync is not None: # If the pending sync is waiting in the distroseries queues, # provide a handy link to there. queue_item = getUtility(IPackageUploadSet).getByPackageCopyJobIDs( (pending_sync.id,)).any() if queue_item is None: description.append("synchronizing") else: url = urlappend( canonical_url(self.context), "+queue?queue_state=%s" % queue_item.status.value) description.append('waiting in <a href="%s">%s</a>' % (url, queue_item.status.name)) return " and ".join(description) + "…"
def _getTemplateParams(self, email, recipient): """See `BaseMailer`.""" params = super(TeamMembershipMailer, self)._getTemplateParams(email, recipient) params["recipient"] = recipient.displayname reason, _ = self._recipients.getReason(email) if reason.recipient_class is not None: params["recipient_class"] = reason.recipient_class params["member"] = self.member.unique_displayname params["membership_invitations_url"] = "%s/+invitation/%s" % ( canonical_url(self.member), self.team.name) params["team"] = self.team.unique_displayname params["team_url"] = canonical_url(self.team) if self.membership is not None: params["membership_url"] = canonical_url(self.membership) if reason.recipient_class == "bulk" and self.reviewer == self.member: params["reviewer"] = "the user" elif self.reviewer is not None: params["reviewer"] = self.reviewer.unique_displayname if self.team.mailing_list is not None: template = get_email_template("team-list-subscribe-block.txt", app="registry") editemails_url = urlappend( canonical_url(getUtility(ILaunchpadRoot)), "~/+editmailinglists") list_instructions = template % {"editemails_url": editemails_url} else: list_instructions = "" params["list_instructions"] = list_instructions params.update(self.extra_params) return params
def http_url(self): """Return the webapp URL for the context `LibraryFileAlias`. Preserve the `LibraryFileAlias.http_url` behaviour for deleted files, returning None. Mask webservice requests if it's the case, so the returned URL will be always relative to the parent webapp URL. """ if self.context.deleted: return None parent_url = canonical_url(self.parent, request=self.request) traversal_url = urlappend(parent_url, '+files') url = urlappend(traversal_url, url_path_quote(self.context.filename.encode('utf-8'))) return url
def assertRequest(self, url_suffix, json_data=None, method=None, **kwargs): [request] = self.requests self.assertThat(request, MatchesStructure.byEquality( url=urlappend(self.endpoint, url_suffix), method=method, **kwargs)) if json_data is not None: self.assertEqual(json_data, json.loads(request.body)) timeline = get_request_timeline(get_current_browser_request()) action = timeline.actions[-1] self.assertEqual("git-hosting-%s" % method.lower(), action.category) self.assertEqual( "/" + url_suffix.split("?", 1)[0], action.detail.split(" ", 1)[0])
def http_url(self): if self.context.deleted: return None url = canonical_url(self.parent.archive, request=self.request) return urlappend( url, '/'.join([ '+sourcefiles', self.parent.source_package_name, self.parent.source_package_version, url_path_quote(self.context.filename.encode('utf-8')) ]))
def render(self): # Reauthentication is called for by a query string parameter. reauth_qs = self.request.query_string_params.get('reauth', ['0']) do_reauth = int(reauth_qs[0]) if self.account is not None and not do_reauth: return AlreadyLoggedInView(self.context, self.request)() # Allow unauthenticated users to have sessions for the OpenID # handshake to work. allowUnauthenticatedSession(self.request) consumer = self._getConsumer() openid_vhost = config.launchpad.openid_provider_vhost timeline_action = get_request_timeline(self.request).start( "openid-association-begin", allvhosts.configs[openid_vhost].rooturl, allow_nested=True) try: self.openid_request = consumer.begin( allvhosts.configs[openid_vhost].rooturl) finally: timeline_action.finish() self.openid_request.addExtension( sreg.SRegRequest(required=['email', 'fullname'])) # Force the Open ID handshake to re-authenticate, using # pape extension's max_auth_age, if the URL indicates it. if do_reauth: self.openid_request.addExtension(pape.Request(max_auth_age=0)) assert not self.openid_request.shouldSendRedirect(), ( "Our fixed OpenID server should not need us to redirect.") # Once the user authenticates with the OpenID provider they will be # sent to the /+openid-callback page, where we log them in, but # once that's done they must be sent back to the URL they were when # they started the login process (i.e. the current URL without the # '+login' bit). To do that we encode that URL as a query arg in the # return_to URL passed to the OpenID Provider starting_url = urllib.urlencode( [('starting_url', self.starting_url.encode('utf-8'))]) trust_root = allvhosts.configs['mainsite'].rooturl return_to = urlappend(trust_root, '+openid-callback') return_to = "%s?%s" % (return_to, starting_url) form_html = self.openid_request.htmlMarkup(trust_root, return_to) # The consumer.begin() call above will insert rows into the # OpenIDAssociations table, but since this will be a GET request, the # transaction would be rolled back, so we need an explicit commit # here. transaction.commit() return form_html
def render(self): # Reauthentication is called for by a query string parameter. reauth_qs = self.request.query_string_params.get('reauth', ['0']) do_reauth = int(reauth_qs[0]) if self.account is not None and not do_reauth: return AlreadyLoggedInView(self.context, self.request)() # Allow unauthenticated users to have sessions for the OpenID # handshake to work. allowUnauthenticatedSession(self.request) consumer = self._getConsumer() openid_vhost = config.launchpad.openid_provider_vhost timeline_action = get_request_timeline(self.request).start( "openid-association-begin", allvhosts.configs[openid_vhost].rooturl, allow_nested=True) try: self.openid_request = consumer.begin( allvhosts.configs[openid_vhost].rooturl) finally: timeline_action.finish() self.openid_request.addExtension( sreg.SRegRequest(required=['email', 'fullname'])) # Force the Open ID handshake to re-authenticate, using # pape extension's max_auth_age, if the URL indicates it. if do_reauth: self.openid_request.addExtension(pape.Request(max_auth_age=0)) assert not self.openid_request.shouldSendRedirect(), ( "Our fixed OpenID server should not need us to redirect.") # Once the user authenticates with the OpenID provider they will be # sent to the /+openid-callback page, where we log them in, but # once that's done they must be sent back to the URL they were when # they started the login process (i.e. the current URL without the # '+login' bit). To do that we encode that URL as a query arg in the # return_to URL passed to the OpenID Provider starting_url = urllib.urlencode([('starting_url', self.starting_url.encode('utf-8'))]) trust_root = allvhosts.configs['mainsite'].rooturl return_to = urlappend(trust_root, '+openid-callback') return_to = "%s?%s" % (return_to, starting_url) form_html = self.openid_request.htmlMarkup(trust_root, return_to) # The consumer.begin() call above will insert rows into the # OpenIDAssociations table, but since this will be a GET request, the # transaction would be rolled back, so we need an explicit commit # here. transaction.commit() return form_html
def assertRequest(self, url_suffix, **kwargs): [request] = self.requests self.assertThat( request, MatchesStructure.byEquality(url=urlappend(self.endpoint, url_suffix), method="GET", **kwargs)) timeline = get_request_timeline(get_current_browser_request()) action = timeline.actions[-1] self.assertEqual("branch-hosting-get", action.category) self.assertEqual("/" + url_suffix.split("?", 1)[0], action.detail.split(" ", 1)[0])
def redirectSubTree(self, target, status=301): """Redirect the subtree to the given target URL.""" while True: nextstep = self.request.stepstogo.consume() if nextstep is None: break target = urlappend(target, nextstep) query_string = self.request.get('QUERY_STRING') if query_string: target = target + '?' + query_string return RedirectionView(target, self.request, status)
def __init__(self, baseurl, xmlrpc_transport=None, internal_xmlrpc_transport=None): super(BugzillaAPI, self).__init__(baseurl) self._bugs = {} self._bug_aliases = {} self.xmlrpc_endpoint = urlappend(self.baseurl, 'xmlrpc.cgi') self.internal_xmlrpc_transport = internal_xmlrpc_transport if xmlrpc_transport is None: self.xmlrpc_transport = UrlLib2Transport(self.xmlrpc_endpoint) else: self.xmlrpc_transport = xmlrpc_transport
def http_url(self): """Return the webapp URL for the context `LibraryFileAlias`. Preserve the `LibraryFileAlias.http_url` behavior for deleted files, returning None. Mask webservice requests if it's the case, so the returned URL will be always relative to the parent webapp URL. """ if self.context.deleted: return None request = get_current_browser_request() if WebServiceLayer.providedBy(request): request = IWebBrowserOriginatingRequest(request) parent_url = canonical_url(self.parent, request=request) traversal_url = urlappend(parent_url, '+files') url = urlappend( traversal_url, url_path_quote(self.context.filename.encode('utf-8'))) return url
def redirect_branch(self): """Redirect /+branch/<foo> to the branch named 'foo'. 'foo' can be the unique name of the branch, or any of the aliases for the branch. If 'foo' resolves to an ICanHasLinkedBranch instance but the linked branch is not yet set, redirect back to the referring page with a suitable notification message. If 'foo' is completely invalid, redirect back to the referring page with a suitable error message. """ # The default target url to go to will be back to the referring page # (in the case that there is an error resolving the branch url). # Note: the http referer may be None if someone has hacked a url # directly rather than following a /+branch/<foo> link. target_url = self.request.getHeader('referer') path = '/'.join(self.request.stepstogo) try: branch, trailing = getUtility(IBranchLookup).getByLPPath(path) target_url = canonical_url(branch) if trailing != '': target_url = urlappend(target_url, trailing) except (NoLinkedBranch) as e: # A valid ICanHasLinkedBranch target exists but there's no # branch or it's not visible. # If are aren't arriving at this invalid branch URL from # another page then we just raise a NotFoundError to generate # a 404, otherwise we end up in a bad recursion loop. The # target url will be None in that case. if target_url is None: raise NotFoundError self.request.response.addNotification( "The target %s does not have a linked branch." % path) except (CannotHaveLinkedBranch, InvalidNamespace, InvalidProductName, NotFoundError) as e: # If are aren't arriving at this invalid branch URL from another # page then we just raise a NotFoundError to generate a 404, # otherwise we end up in a bad recursion loop. The target url will # be None in that case. if target_url is None: raise NotFoundError error_msg = str(e) if error_msg == '': error_msg = "Invalid branch lp:%s." % path self.request.response.addErrorNotification(error_msg) return self.redirectSubTree(target_url)
def redirect_branch(self): """Redirect /+branch/<foo> to the branch named 'foo'. 'foo' can be the unique name of the branch, or any of the aliases for the branch. If 'foo' resolves to an ICanHasLinkedBranch instance but the linked branch is not yet set, redirect back to the referring page with a suitable notification message. If 'foo' is completely invalid, redirect back to the referring page with a suitable error message. """ # The default target url to go to will be back to the referring page # (in the case that there is an error resolving the branch url). # Note: the http referer may be None if someone has hacked a url # directly rather than following a /+branch/<foo> link. target_url = self.request.getHeader('referer') path = '/'.join(self.request.stepstogo) try: branch, trailing = getUtility(IBranchLookup).getByLPPath(path) target_url = canonical_url(branch) if trailing != '': target_url = urlappend(target_url, trailing) except (NoLinkedBranch) as e: # A valid ICanHasLinkedBranch target exists but there's no # branch or it's not visible. # If are aren't arriving at this invalid branch URL from # another page then we just raise a NotFoundError to generate # a 404, otherwise we end up in a bad recursion loop. The # target url will be None in that case. if target_url is None: raise NotFoundError self.request.response.addNotification( "The target %s does not have a linked branch." % path) except (CannotHaveLinkedBranch, InvalidNamespace, InvalidProductName, NotFoundError) as e: # If are aren't arriving at this invalid branch URL from another # page then we just raise a NotFoundError to generate a 404, # otherwise we end up in a bad recursion loop. The target url will # be None in that case. if target_url is None: raise NotFoundError error_msg = str(e) if error_msg == '': error_msg = "Invalid branch lp:%s." % path self.request.response.addErrorNotification(error_msg) return self.redirectSubTree(target_url)
def __init__(self, baseurl, xmlrpc_transport=None, internal_xmlrpc_transport=None): super(BugzillaAPI, self).__init__(baseurl) self._bugs = {} self._bug_aliases = {} self.xmlrpc_endpoint = urlappend(self.baseurl, 'xmlrpc.cgi') self.internal_xmlrpc_transport = internal_xmlrpc_transport if xmlrpc_transport is None: self.xmlrpc_transport = UrlLib2Transport(self.xmlrpc_endpoint) else: self.xmlrpc_transport = xmlrpc_transport
def requestAuthorization(cls, snap, request): """Begin the process of authorizing uploads of a snap package.""" try: sso_caveat_id = snap.beginAuthorization() base_url = canonical_url(snap, view_name='+authorize') login_url = urlappend(base_url, '+login') login_url += '?%s' % urlencode([ ('macaroon_caveat_id', sso_caveat_id), ('discharge_macaroon_action', 'field.actions.complete'), ('discharge_macaroon_field', 'field.discharge_macaroon'), ]) return login_url except CannotAuthorizeStoreUploads as e: request.response.addInfoNotification(unicode(e)) request.response.redirect(canonical_url(snap)) return
def refreshDischargeMacaroon(cls, snap): """See `ISnapStoreClient`.""" assert config.launchpad.openid_provider_root is not None assert snap.store_secrets is not None refresh_url = urlappend( config.launchpad.openid_provider_root, "api/v2/tokens/refresh") data = {"discharge_macaroon": snap.store_secrets["discharge"]} try: response = urlfetch(refresh_url, method="POST", json=data) response_data = response.json() if "discharge_macaroon" not in response_data: raise BadRefreshResponse(response.text) # Set a new dict here to avoid problems with security proxies. new_secrets = dict(snap.store_secrets) new_secrets["discharge"] = response_data["discharge_macaroon"] snap.store_secrets = new_secrets except requests.HTTPError as e: raise cls._makeSnapStoreError(BadRefreshResponse, e)
def __init__(self, baseurl, xmlrpc_transport=None, internal_xmlrpc_transport=None, cookie_jar=None): super(TracLPPlugin, self).__init__(baseurl) if cookie_jar is None: cookie_jar = CookieJar() if xmlrpc_transport is None: xmlrpc_transport = UrlLib2Transport(baseurl, cookie_jar) self._cookie_jar = cookie_jar self._xmlrpc_transport = xmlrpc_transport self._internal_xmlrpc_transport = internal_xmlrpc_transport xmlrpc_endpoint = urlappend(self.baseurl, 'xmlrpc') self._server = xmlrpclib.ServerProxy( xmlrpc_endpoint, transport=self._xmlrpc_transport) self.url_opener = urllib2.build_opener( urllib2.HTTPCookieProcessor(cookie_jar))
def __init__(self, baseurl, xmlrpc_transport=None, internal_xmlrpc_transport=None, cookie_jar=None): super(TracLPPlugin, self).__init__(baseurl) if cookie_jar is None: cookie_jar = RequestsCookieJar() if xmlrpc_transport is None: xmlrpc_transport = RequestsTransport(baseurl, cookie_jar) self._cookie_jar = cookie_jar self._xmlrpc_transport = xmlrpc_transport self._internal_xmlrpc_transport = internal_xmlrpc_transport xmlrpc_endpoint = urlappend(self.baseurl, 'xmlrpc') self._server = xmlrpclib.ServerProxy(xmlrpc_endpoint, transport=self._xmlrpc_transport)
def __call__(self): if IUnauthenticatedPrincipal.providedBy(self.request.principal): if 'loggingout' in self.request.form: target = '%s?loggingout=1' % self.request.URL[-2] self.request.response.redirect(target) return '' if self.request.method == 'POST': # If we got a POST then that's a problem. We can only # redirect with a GET, so when we redirect after a successful # login, the wrong method would be used. # If we get a POST here, it is an application error. We # must ensure that form pages require the same rights # as the pages that process those forms. So, we should never # need to newly authenticate on a POST. self.request.response.setStatus(500) # Internal Server Error self.request.response.setHeader('Content-type', 'text/plain') return ('Application error. Unauthenticated user POSTing to ' 'page that requires authentication.') # If we got any query parameters, then preserve them in the # new URL. Except for the BrowserNotifications current_url = self.request.getURL() while True: nextstep = self.request.stepstogo.consume() if nextstep is None: break current_url = urlappend(current_url, nextstep) query_string = self.request.get('QUERY_STRING', '') if query_string: query_string = '?' + query_string target = self.getRedirectURL(current_url, query_string) # A dance to assert that we want to break the rules about no # unauthenticated sessions. Only after this next line is it safe # to use the ``addInfoNotification`` method. allowUnauthenticatedSession(self.request) self.request.response.redirect(target) # Maybe render page with a link to the redirection? return '' else: self.request.response.setStatus(403) # Forbidden return self.template()
def listChannels(cls): """See `ISnapStoreClient`.""" if config.snappy.store_search_url is None: return _default_store_channels channels = None memcache_client = getUtility(IMemcacheClient) search_host = urlsplit(config.snappy.store_search_url).hostname memcache_key = ("%s:channels" % search_host).encode("UTF-8") cached_channels = memcache_client.get(memcache_key) if cached_channels is not None: try: channels = json.loads(cached_channels) except JSONDecodeError: log.exception( "Cannot load cached channels for %s; deleting" % search_host) memcache_client.delete(memcache_key) if (channels is None and not getFeatureFlag(u"snap.disable_channel_search")): path = "api/v1/channels" timeline = cls._getTimeline() if timeline is not None: action = timeline.start("store-search-get", "/" + path) channels_url = urlappend(config.snappy.store_search_url, path) try: response = urlfetch( channels_url, headers={"Accept": "application/hal+json"}) except requests.HTTPError as e: raise cls._makeSnapStoreError(BadSearchResponse, e) finally: if timeline is not None: action.finish() channels = response.json().get("_embedded", {}).get( "clickindex:channel", []) expire_time = time.time() + 60 * 60 * 24 memcache_client.set( memcache_key, json.dumps(channels), expire_time) if channels is None: channels = _default_store_channels return channels
def __call__(self): if IUnauthenticatedPrincipal.providedBy(self.request.principal): if 'loggingout' in self.request.form: target = '%s?loggingout=1' % self.request.URL[-2] self.request.response.redirect(target) return '' if self.request.method == 'POST': # If we got a POST then that's a problem. We can only # redirect with a GET, so when we redirect after a successful # login, the wrong method would be used. # If we get a POST here, it is an application error. We # must ensure that form pages require the same rights # as the pages that process those forms. So, we should never # need to newly authenticate on a POST. self.request.response.setStatus(500) # Internal Server Error self.request.response.setHeader('Content-type', 'text/plain') return ('Application error. Unauthenticated user POSTing to ' 'page that requires authentication.') # If we got any query parameters, then preserve them in the # new URL. Except for the BrowserNotifications current_url = self.request.getURL() while True: nextstep = self.request.stepstogo.consume() if nextstep is None: break current_url = urlappend(current_url, nextstep) query_string = self.request.get('QUERY_STRING', '') if query_string: query_string = '?' + query_string target = self.getRedirectURL(current_url, query_string) # A dance to assert that we want to break the rules about no # unauthenticated sessions. Only after this next line is it safe # to use the ``addInfoNotification`` method. allowUnauthenticatedSession(self.request) self.request.response.redirect(target) # Maybe render page with a link to the redirection? return '' else: self.request.response.setStatus(403) # Forbidden return self.template()
def __init__(self, baseurl, xmlrpc_transport=None, internal_xmlrpc_transport=None, cookie_jar=None): super(TracLPPlugin, self).__init__(baseurl) if cookie_jar is None: cookie_jar = CookieJar() if xmlrpc_transport is None: xmlrpc_transport = UrlLib2Transport(baseurl, cookie_jar) self._cookie_jar = cookie_jar self._xmlrpc_transport = xmlrpc_transport self._internal_xmlrpc_transport = internal_xmlrpc_transport xmlrpc_endpoint = urlappend(self.baseurl, 'xmlrpc') self._server = xmlrpclib.ServerProxy(xmlrpc_endpoint, transport=self._xmlrpc_transport) self.url_opener = urllib2.build_opener( urllib2.HTTPCookieProcessor(cookie_jar))
def __init__(self, baseurl, xmlrpc_transport=None, internal_xmlrpc_transport=None): super(BugzillaAPI, self).__init__(baseurl) self._bugs = {} self._bug_aliases = {} self.xmlrpc_endpoint = urlappend(self.baseurl, 'xmlrpc.cgi') self.internal_xmlrpc_transport = internal_xmlrpc_transport if xmlrpc_transport is None: self.xmlrpc_transport = RequestsTransport(self.xmlrpc_endpoint) else: self.xmlrpc_transport = xmlrpc_transport try: self.credentials except BugTrackerAuthenticationError: pass else: alsoProvides(self, ISupportsBackLinking) alsoProvides(self, ISupportsCommentPushing)
def requestPackageUploadPermission(cls, snappy_series, snap_name): assert config.snappy.store_url is not None request_url = urlappend(config.snappy.store_url, "dev/api/acl/") request = get_current_browser_request() timeline_action = get_request_timeline(request).start( "request-snap-upload-macaroon", "%s/%s" % (snappy_series.name, snap_name), allow_nested=True) try: response = urlfetch( request_url, method="POST", json={ "packages": [ {"name": snap_name, "series": snappy_series.name}], "permissions": ["package_upload"], }) response_data = response.json() if "macaroon" not in response_data: raise BadRequestPackageUploadResponse(response.text) return response_data["macaroon"] except requests.HTTPError as e: raise cls._makeSnapStoreError(BadRequestPackageUploadResponse, e) finally: timeline_action.finish()
def href(self): return urlappend(self.rooturl, 'bugs/' + str(self.context.bug.id) + '/bug.atom')
def href(self): if IAnnouncementSet.providedBy(self.context): return urlappend(self.rooturl, 'announcements.atom') else: return urlappend(canonical_url(self.context, rootsite='feeds'), 'announcements.atom')
def href(self): return urlappend(self.rooturl, 'announcements.atom')
def href(self): return urlappend(canonical_url(self.context, rootsite="feeds"), 'branch.atom')
def href(self): return urlappend(canonical_url(self.context, rootsite="feeds"), 'revisions.atom')
def test_trailing_path_redirect(self): # If there are any trailing path segments after the branch identifier, # these stick around at the redirected URL. branch = self.factory.makeAnyBranch() path = urlappend(branch.unique_name, '+edit') self.assertRedirects(path, canonical_url(branch, view_name='+edit'))
def combine_url(base, subdir, filename): """Combine a URL from the three parts returned by walk().""" subdir_url = urljoin(base, subdir) # The "filename" component must be appended to the resulting URL. return urlappend(subdir_url, filename)
def parent_changelog_url(self, distroseriesdifference): """The URL to the /parent/series/+source/package/+changelog """ distro = distroseriesdifference.parent_series.distribution dsp = distro.getSourcePackage( distroseriesdifference.source_package_name) return urlappend(canonical_url(dsp), '+changelog')
def redirect_buildfarm(self): """Redirect old /+builds requests to new URL, /builders.""" new_url = '/builders' return self.redirectSubTree( urlappend(new_url, '/'.join(self.request.stepstogo)))
def href(self): return urlappend(canonical_url(self.context, rootsite='feeds'), 'latest-bugs.atom')
def notify_team_join(event): """Notify team admins that someone has asked to join the team. If the team's policy is Moderated, the email will say that the membership is pending approval. Otherwise it'll say that the person has joined the team and who added that person to the team. """ person = event.person team = event.team membership = getUtility(ITeamMembershipSet).getByPersonAndTeam( person, team) assert membership is not None approved, admin, proposed = [ TeamMembershipStatus.APPROVED, TeamMembershipStatus.ADMIN, TeamMembershipStatus.PROPOSED] admin_addrs = team.getTeamAdminsEmailAddresses() from_addr = format_address( team.displayname, config.canonical.noreply_from_address) reviewer = membership.proposed_by if reviewer != person and membership.status in [approved, admin]: reviewer = membership.reviewed_by # Somebody added this person as a member, we better send a # notification to the person too. member_addrs = get_contact_email_addresses(person) headers = {} if person.is_team: templatename = 'new-member-notification-for-teams.txt' subject = '%s joined %s' % (person.name, team.name) header_rational = "Indirect member (%s)" % team.name footer_rationale = ( "You received this email because " "%s is the new member." % person.name) else: templatename = 'new-member-notification.txt' subject = 'You have been added to %s' % team.name header_rational = "Member (%s)" % team.name footer_rationale = ( "You received this email because you are the new member.") if team.mailing_list is not None: template = get_email_template( 'team-list-subscribe-block.txt', app='registry') editemails_url = urlappend( canonical_url(getUtility(ILaunchpadRoot)), 'people/+me/+editemails') list_instructions = template % dict(editemails_url=editemails_url) else: list_instructions = '' template = get_email_template(templatename, app='registry') replacements = { 'reviewer': '%s (%s)' % (reviewer.displayname, reviewer.name), 'team_url': canonical_url(team), 'member': '%s (%s)' % (person.displayname, person.name), 'team': '%s (%s)' % (team.displayname, team.name), 'list_instructions': list_instructions, } headers = {'X-Launchpad-Message-Rationale': header_rational} for address in member_addrs: recipient = getUtility(IPersonSet).getByEmail(address) replacements['recipient_name'] = recipient.displayname send_team_email( from_addr, address, subject, template, replacements, footer_rationale, headers) # The member's email address may be in admin_addrs too; let's remove # it so the member don't get two notifications. admin_addrs = set(admin_addrs).difference(set(member_addrs)) # Yes, we can have teams with no members; not even admins. if not admin_addrs: return # Open teams do not notify admins about new members. if team.membership_policy == TeamMembershipPolicy.OPEN: return replacements = { 'person_name': "%s (%s)" % (person.displayname, person.name), 'team_name': "%s (%s)" % (team.displayname, team.name), 'reviewer_name': "%s (%s)" % (reviewer.displayname, reviewer.name), 'url': canonical_url(membership)} headers = {} if membership.status in [approved, admin]: template = get_email_template( 'new-member-notification-for-admins.txt', app='registry') subject = '%s joined %s' % (person.name, team.name) elif membership.status == proposed: # In the UI, a user can only propose himself or a team he # admins. Some users of the REST API have a workflow, where # they propose users that are designated as mentees (Bug 498181). if reviewer != person: headers = {"Reply-To": reviewer.preferredemail.email} template = get_email_template( 'pending-membership-approval-for-third-party.txt', app='registry') else: headers = {"Reply-To": person.preferredemail.email} template = get_email_template( 'pending-membership-approval.txt', app='registry') subject = "%s wants to join" % person.name else: raise AssertionError( "Unexpected membership status: %s" % membership.status) for address in admin_addrs: recipient = getUtility(IPersonSet).getByEmail(address) replacements['recipient_name'] = recipient.displayname if recipient.is_team: header_rationale = 'Admin (%s via %s)' % ( team.name, recipient.name) footer_rationale = ( "you are an admin of the %s team\n" "via the %s team." % ( team.displayname, recipient.displayname)) elif recipient == team.teamowner: header_rationale = 'Owner (%s)' % team.name footer_rationale = ( "you are the owner of the %s team." % team.displayname) else: header_rationale = 'Admin (%s)' % team.name footer_rationale = ( "you are an admin of the %s team." % team.displayname) footer = 'You received this email because %s' % footer_rationale headers['X-Launchpad-Message-Rationale'] = header_rationale send_team_email( from_addr, address, subject, template, replacements, footer, headers)