def _set_response(self, environ, start_response, oid_request, identity_uri): '''Add Attribute exchange parameters to the response if OpenID Relying Party requested them @type oid_request: openid.server.server.CheckIDRequest @param oid_request: OpenID Check ID request @rtype: basestring @return: WSGI response ''' # Process any request for additional attributes contained in the query # from the Relying Party oid_response = oid_request.answer(True, identity=identity_uri) try: self._add_ax_response(oid_request, oid_response) except (OpenIDProviderMissingRequiredAXAttrs, OpenIDProviderMissingAXResponseHandler): log.error('The requesting Relying Party requires additional ' 'attributes which this site isn\'t configured to ' 'provide.') raise HTTPUnauthorized() except OpenIDProviderReloginRequired, e: log.error('An error occurred setting return attribute parameters ' 'required by the Relying Party requesting your ID.') raise HTTPUnauthorized()
def build_authentication(self): head = WWW_AUTHENTICATE.tuples('Basic realm="%s"' % self.realm) if self._rc_auth_http_code and self._rc_auth_http_code == '403': # return 403 if alternative http return code is specified in # RhodeCode config return HTTPForbidden(headers=head) return HTTPUnauthorized(headers=head)
def identify(self, environ): path_info = environ['PATH_INFO'] query = parse_dict_querystring(environ) # This will handle the logout request. if path_info == self._logout_url: # set in environ for self.challenge() to find later environ['repoze.who.application'] = HTTPUnauthorized() return None elif path_info == self._login_url: form = parse_formvars(environ) form.update(query) try: credentials = { 'login': form['login'], 'password': form['password'] } except KeyError: credentials = None def auth_resp(environ, start_response): import json resp = {"success": True} resp_str = json.dumps(resp) content_length = CONTENT_LENGTH.tuples(str(len(resp_str))) content_type = CONTENT_TYPE.tuples('application/json') headers = content_length + content_type start_response('200 OK', headers) return [resp_str] environ['repoze.who.application'] = auth_resp return credentials
def identify(self, environ): if environ['PATH_INFO'] == self.login_handler_path: ## We are on the URL where repoze.who processes authentication. ## form = parse_formvars(environ) try: credentials = { 'login': form['login'], 'password': form['password'] } except KeyError: credentials = None # destination is the post-login page destination = self._get_full_path(self.post_login_url, environ) # all variables from query string are kept in query string destination = self._replace_qs(destination, environ.get('QUERY_STRING')) environ['repoze.who.application'] = HTTPFound(destination) return credentials elif environ['PATH_INFO'] == self.logout_handler_path: ## We are on the URL where repoze.who logs the user out. ## # let's throw an exception to get challenged environ['repoze.who.application'] = HTTPUnauthorized() return None
def secret_for_others(self, environ, start_response): r"""This can not be accessed from oauth""" if not not_oauth().is_met(environ): start_response('401 ', [('Content-Type', 'text/plain')]) return HTTPUnauthorized() start_response('200 OK', [('Content-Type', 'text/plain')]) return ['This is for all except oauth']
def secret_for_app1(self, environ, start_response): r"""This can be accessed by app1 consumer only""" if not is_consumer('app1').is_met(environ): start_response('401 ', [('Content-Type', 'text/plain')]) return HTTPUnauthorized() start_response('200 OK', [('Content-Type', 'text/plain')]) return ['This is a secret for app1 only']
def test_call_200_identity_reset(self): environ = self._makeEnviron() headers = [('a', '1')] new_identity = {'user_id':'foo', 'password':'******'} app = DummyIdentityResetApp('200 OK', headers, new_identity) from paste.httpexceptions import HTTPUnauthorized challenge_app = HTTPUnauthorized() challenge = DummyChallenger(challenge_app) challengers = [ ('challenge', challenge) ] credentials = {'login':'******', 'password':'******'} identifier = DummyIdentifier(credentials) identifiers = [ ('identifier', identifier) ] authenticator = DummyAuthenticator() authenticators = [ ('authenticator', authenticator) ] mw = self._makeOne(app=app, challengers=challengers, identifiers=identifiers, authenticators=authenticators) start_response = DummyStartResponse() result = mw(environ, start_response) self.assertEqual(environ.get('challenged'), None) self.assertEqual(identifier.forgotten, False) new_credentials = identifier.credentials.copy() new_credentials['login'] = '******' new_credentials['password'] = '******' # @@ unfuck ## self.assertEqual(identifier.remembered, new_credentials) self.assertEqual(environ['REMOTE_USER'], 'chris')
def identify(self, environ): path_info = environ['PATH_INFO'] query = parse_dict_querystring(environ) if path_info == self.logout_handler_path: # we've been asked to perform a logout form = parse_formvars(environ) form.update(query) referer = environ.get('HTTP_REFERER', '/') environ['repoze.who.application'] = HTTPUnauthorized() # invalidate the session identity = environ.get('repoze.who.identity') if identity: self.forget(environ, identity) return None elif path_info == self.login_handler_path: # we've been asked to perform a login form = parse_formvars(environ) form.update(query) credentials = self._getCredentials(form) referer = environ.get('HTTP_REFERER', '/') environ['repoze.who.application'] = HTTPFound(referer) if 'login' not in credentials or 'password' not in credentials: return None return credentials
def challenge(self, environ, status, app_headers, forget_headers): """Return a 401 page unconditionally.""" headers = app_headers + forget_headers # The HTTP status code and reason may not be the default ones: status_parts = _HTTP_STATUS_PATTERN.search(status) if status_parts: reason = status_parts.group('reason') code = int(status_parts.group('code')) else: reason = 'HTTP Unauthorized' code = 401 # Building the response: response = HTTPUnauthorized(headers=headers) response.title = reason response.code = code return response
def identify(self, environ): path_info = environ['PATH_INFO'] query = parse_dict_querystring(environ) if path_info == self.logout_handler_path: # we've been asked to perform a logout form = parse_formvars(environ) form.update(query) referer = environ.get('HTTP_REFERER', '/') environ['repoze.who.application'] = HTTPUnauthorized() return None elif path_info == self.login_handler_path: # we've been asked to perform a login form = parse_formvars(environ) form.update(query) try: max_age = form.get('max_age', None) credentials = { 'login': form['login'], 'password': form['password'], 'realm': form['realm'], } except KeyError: credentials = None if credentials is not None: max_age = form.get('max_age', None) if max_age is not None: credentials['max_age'] = max_age referer = environ.get('HTTP_REFERER', '/') environ['repoze.who.application'] = HTTPFound(referer) return credentials
def challenge(self, environ, status, app_headers, forget_headers): """Return a 401 page unconditionally.""" headers = app_headers + forget_headers remove_header(headers, 'content-length') # The HTTP status code and reason may not be the default ones: status_parts = _HTTP_STATUS_PATTERN.search(status) if status_parts: reason = status_parts.group('reason') code = int(status_parts.group('code')) else: reason = 'HTTP Unauthorized' code = 401 # Building the response: response = HTTPUnauthorized(headers=headers) response.title = reason response.code = code return response
def about(self): if request.environ.get('repoze.who.identity') == None: raise HTTPUnauthorized() user = request.environ['repoze.who.identity']['repoze.who.userid'] flash('Your Distinguished Name (DN) is "%s"' % user) # Passing the metadata metadata = request.environ['repoze.who.identity'] return dict(metadata=metadata.items())
def challenge(self, environ, status, app_headers, forget_headers): r"""If the request failed due to invalid or insufficient parameters or permissions return a WWW-Authenticate header with the realm. """ # Add the WWW-Authenticate header headers = WWW_AUTHENTICATE.tuples('OAuth realm="%s"' % self.realm) if headers[0] not in forget_headers: headers += forget_headers return HTTPUnauthorized(headers=headers)
def build_authentication(self): head = WWW_AUTHENTICATE.tuples('Basic realm="%s"' % self.realm) if self._rc_auth_http_code and not self.initial_call: # return alternative HTTP code if alternative http return code # is specified in RhodeCode config, but ONLY if it's not the # FIRST call custom_response_klass = self._get_response_from_code( self._rc_auth_http_code) return custom_response_klass(headers=head) return HTTPUnauthorized(headers=head)
def _authenticate(self, environ, username, password): self.session = environ.get(self.session_mware_environ_keyname, {}) if (OpenIDProviderMiddleware.USERNAME_SESSION_KEYNAME in self.session): # user is already logged in return identity_uri = None if None in (username, password): raise HTTPUnauthorized() # Invoke custom authentication interface plugin try: self._authN.logon(environ, identity_uri, username, password) except AuthNInterfaceError, e: log.error("Authentication error: %s", traceback.format_exc()) raise HTTPUnauthorized()
def add_metadata(self, environ, identity): """ Call the parent MD provider. :raises HTTPUnauthorized: If the user account no longer exists. """ super(SQLAlchemyStrictUserMDPlugin, self).add_metadata(environ, identity) if identity['user'] is None: # The user has been deleted from the database raise HTTPUnauthorized()
def build_authentication(self, stale = ''): """ builds the authentication error """ nonce = md5.md5("%s:%s" % (time.time(),random.random())).hexdigest() opaque = md5.md5("%s:%s" % (time.time(),random.random())).hexdigest() self.nonce[nonce] = None parts = { 'realm': self.realm, 'qop': 'auth', 'nonce': nonce, 'opaque': opaque } if stale: parts['stale'] = 'true' head = ", ".join(['%s="%s"' % (k,v) for (k,v) in parts.items()]) head = [("WWW-Authenticate", 'Digest %s' % head)] return HTTPUnauthorized(headers=head)
def test_call_401_no_identifiers(self): environ = self._makeEnviron() headers = [('a', '1')] app = DummyWorkingApp('401 Unauthorized', headers) from paste.httpexceptions import HTTPUnauthorized challenge_app = HTTPUnauthorized() challenge = DummyChallenger(challenge_app) challengers = [ ('challenge', challenge) ] mw = self._makeOne(app=app, challengers=challengers) start_response = DummyStartResponse() result = mw(environ, start_response) self.assertEqual(environ['challenged'], challenge_app) self.failUnless(result[0].startswith('401 Unauthorized\r\n'))
def _resolve_and_validate_identity_uri(self, environ, oid_request, username): '''Get OpenID identity URI and check against credentials provided ''' if oid_request.idSelect(): # ID select mode enables the user to request specifying # their OpenID Provider without giving a personal user URL try: user_identifiers = self._authN.username2UserIdentifiers( environ, username) except AuthNInterfaceInvalidCredentials: log.error("No username %r matching an OpenID URL: %s", username, traceback.format_exc()) raise if not isinstance(user_identifiers, (list, tuple)): raise TypeError("Unexpected type %r returned from %r for " "user identifiers; expecting list or tuple" % (type(user_identifiers), type(self._authN))) # FIXME: Assume the *first* user identifier entry is the # one to use. The user could have multiple identifiers # but in practice it's more manageable to have a single one identity_uri = self.createIdentityURI(self.identityUriTmpl, user_identifiers[0]) else: # Get the unique user identifier from the user's OpenID URL identity_uri = oid_request.identity # Check the username used to login with matches the identity URI # given. This check is essential otherwise a user could impersonate # someone else with an account with this provider try: user_identifiers = self._authN.username2UserIdentifiers( environ, username) except AuthNInterfaceInvalidCredentials: log.error("No username %r matching an OpenID URL: %s", username, traceback.format_exc()) raise expected_identity_uri = self.createIdentityURI( self.identityUriTmpl, user_identifiers[0]) if identity_uri != expected_identity_uri: log.error("OpenID given %r, doesn't match the expected " "OpenID %r for this account name %r" % (identity_uri, expected_identity_uri, username)) raise HTTPUnauthorized() return identity_uri
def challenge(self, environ, status, app_headers, forget_headers): cookies = [(h, v) for (h, v) in app_headers if h.lower() == 'set-cookie'] if not forget_headers: return HTTPUnauthorized() def auth_form(environ, start_response): towrite = "Challenging this" content_length = CONTENT_LENGTH.tuples(str(len(towrite))) content_type = CONTENT_TYPE.tuples('text/html') headers = content_length + content_type + forget_headers start_response('200 OK', headers) return [towrite] return auth_form
def _complete_login(self, environ, start_response): """Complete the OpenID authentication process. Here we handle the result of the OpenID process. If the process succeeded, we record the username in the session and redirect the user to the page they were trying to view that triggered the login attempt. In the various failures cases we return a 401 Unauthorized response with a brief explanation of what went wrong. """ query = dict(parse_querystring(environ)) # Passing query['openid.return_to'] here is massive cheating, but # given we control the endpoint who cares. response = self._make_consumer(environ).complete( query, query['openid.return_to']) if response.status == SUCCESS: self.log.error('open id response: SUCCESS') sreg_info = SRegResponse.fromSuccessResponse(response) if not sreg_info: self.log.error('sreg_info is None.') exc = HTTPUnauthorized() exc.explanation = ( "You don't have a Launchpad account. Check that you're " "logged in as the right user, or log into Launchpad and try " "again.") raise exc environ[self.session_var]['user'] = sreg_info['nickname'] raise HTTPMovedPermanently(query['back_to']) elif response.status == FAILURE: self.log.error('open id response: FAILURE: %s', response.message) exc = HTTPUnauthorized() exc.explanation = response.message raise exc elif response.status == CANCEL: self.log.error('open id response: CANCEL') exc = HTTPUnauthorized() exc.explanation = "Authentication cancelled." raise exc else: self.log.error('open id response: UNKNOWN') exc = HTTPUnauthorized() exc.explanation = "Unknown OpenID response." raise exc
def test_call_401_challenger_and_identifier_no_authenticator(self): environ = self._makeEnviron() headers = [('a', '1')] app = DummyWorkingApp('401 Unauthorized', headers) from paste.httpexceptions import HTTPUnauthorized challenge_app = HTTPUnauthorized() challenge = DummyChallenger(challenge_app) challengers = [ ('challenge', challenge) ] credentials = {'login':'******', 'password':'******'} identifier = DummyIdentifier(credentials) identifiers = [ ('identifier', identifier) ] mw = self._makeOne(app=app, challengers=challengers, identifiers=identifiers) start_response = DummyStartResponse() result = mw(environ, start_response) self.assertEqual(environ['challenged'], challenge_app) self.failUnless(result[0].startswith('401 Unauthorized\r\n')) self.assertEqual(identifier.forgotten, False) self.assertEqual(environ.get('REMOTE_USER'), None)
def authenticate(self, environ, identity): # Detect the request type rtype = self._detect_request_type(environ, identity) # Prepare the common environment for the actions env = dict(environ=environ, identity=identity if identity else {}) failed = False # Iterate through the actions of the request type and let them validate # and modify the common environment for validator in self.request_types[rtype]: if not validator(self, env): failed = True break if failed: # One validator failed. The failed validator may prevent the 401 if # it sets env['throw_401'] = False. If not then DIE! throw_401 = env.get('throw_401', True) if rtype in ('request-token', 'access-token') and throw_401: # repoze will replace the downstream app with what we set in # repoze.who.application. This is a standard way to replace the # downstream app in IAuthenticators environ['repoze.who.application'] = HTTPUnauthorized() return consumer = env.get('consumer') if consumer: # If the validators found a consumer then remember it in the environ identity['repoze.who.consumerkey'] = consumer.key identity['consumer'] = consumer token = env.get('token') if token: # If a token exists then it's a 3-legged request - return the # associated userid return token.userid else: # Otherwise it's a 2-legged request - return the consumer key return 'consumer:%s' % consumer.key
def test_call_app_doesnt_call_start_response(self): environ = self._makeEnviron() headers = [('a', '1')] app = DummyGeneratorApp('200 OK', headers) from paste.httpexceptions import HTTPUnauthorized challenge_app = HTTPUnauthorized() challenge = DummyChallenger(challenge_app) challengers = [ ('challenge', challenge) ] credentials = {'login':'******', 'password':'******'} identifier = DummyIdentifier(credentials) identifiers = [ ('identifier', identifier) ] authenticator = DummyAuthenticator() authenticators = [ ('authenticator', authenticator) ] mdprovider = DummyMDProvider({'foo':'bar'}) mdproviders = [ ('mdprovider', mdprovider) ] mw = self._makeOne(app=app, challengers=challengers, identifiers=identifiers, authenticators=authenticators, mdproviders=mdproviders) start_response = DummyStartResponse() result = mw(environ, start_response) # metadata self.assertEqual(environ['repoze.who.identity']['foo'], 'bar')
def test_call_200_challenger_and_identifier_and_authenticator(self): environ = self._makeEnviron() headers = [('a', '1')] app = DummyWorkingApp('200 OK', headers) from paste.httpexceptions import HTTPUnauthorized challenge_app = HTTPUnauthorized() challenge = DummyChallenger(challenge_app) challengers = [ ('challenge', challenge) ] credentials = {'login':'******', 'password':'******'} identifier = DummyIdentifier(credentials) identifiers = [ ('identifier', identifier) ] authenticator = DummyAuthenticator() authenticators = [ ('authenticator', authenticator) ] mw = self._makeOne(app=app, challengers=challengers, identifiers=identifiers, authenticators=authenticators) start_response = DummyStartResponse() result = mw(environ, start_response) self.assertEqual(environ.get('challenged'), None) self.assertEqual(identifier.forgotten, False) # @@ figure out later ## self.assertEqual(dict(identifier.remembered)['login'], dict(identifier.credentials)['login']) ## self.assertEqual(dict(identifier.remembered)['password'], dict(identifier.credentials)['password']) self.assertEqual(environ['REMOTE_USER'], 'chris')
log.error('The requesting Relying Party requires additional ' 'attributes which this site isn\'t configured to ' 'provide.') raise HTTPUnauthorized() except OpenIDProviderReloginRequired, e: log.error('An error occurred setting return attribute parameters ' 'required by the Relying Party requesting your ID.') raise HTTPUnauthorized() except Exception, e: log.error( "%s type exception raised setting additional attributes " " in the response: %s", e.__class__.__name__, traceback.format_exc()) raise HTTPUnauthorized() webresponse = self.oidserver.encodeResponse(oid_response) hdr = webresponse.headers.items() # If the content length exceeds the maximum to represent on a URL, # it's rendered as a form instead # FIXME: Got rid out oid_response.renderAsForm() test as it doesn't # give consistent answers. # # The FORM_MATCH_TEXT test detects whether the response needs to be # wrapped in the FORM_RESP_WRAPPER_TMPL Javascript. This is only # needed when this Provider is return key/values pairs back to the # RP as a POST'ed form if webresponse.body.startswith( OpenIDProviderMiddleware.FORM_MATCH_TEXT):
def build_authentication(self): head = WWW_AUTHENTICATE.tuples('Basic realm="%s"' % self.realm) return HTTPUnauthorized(headers=head)
def challenge(self, environ, status, app_headers, forget_headers): head = self._get_wwwauth() if head[0] not in forget_headers: head = head + forget_headers return HTTPUnauthorized(headers=head)
def challenge(self, environ, status, app_headers, forget_headers): return HTTPUnauthorized()
def __call__(self, environ, start_response): """Authenticate based HTTP header elements as specified by the HTTP Basic Authentication spec. @param environ: WSGI environ @type environ: dict-like type @param start_response: WSGI start response function @type start_response: function @return: response @rtype: iterable @raise HttpBasicAuthMiddlewareConfigError: no authentication callback found in environ """ log.debug("HttpBasicAuthNMiddleware.__call__ ...") if (not self._path_match(environ) or not self._http_hdr_field_match(environ)): return self.__app(environ, start_response) # Pick up authentication callback from local variable or special key # in environ set by upstream middleware if self.authentication_callback is None: authenticate_func = environ.get(self.authn_func_environ_keyname) else: authenticate_func = self.authentication_callback if authenticate_func is None: # HTTP 500 default is right for this error raise HttpBasicAuthMiddlewareConfigError("No authentication " "function set in environ") def start_response_wrapper(status, headers, exec_info=None): """Ensure Authentication realm is included with 401 responses""" status_code = int(status.split()[0]) if status_code == httplib.UNAUTHORIZED: authn_realm_hdrFound = False for name, val in headers: if (name.lower() == self.__class__.AUTHENTICATE_HDR_FIELDNAME_LOWER): authn_realm_hdrFound = True break if not authn_realm_hdrFound: # Nb. realm requires double quotes according to RFC authn_realm_hdr = ( self.__class__.AUTHENTICATE_HDR_FIELDNAME, self.__class__.AUTHN_HDR_FORMAT % (self.__class__.AUTHN_SCHEME_HDR_FIELDNAME, self.realm)) headers.append(authn_realm_hdr) return start_response(status, headers) username, password = self.parse_credentials(environ) # Call authentication middleware/application. try: response = authenticate_func(environ, start_response_wrapper, username, password) except HTTPUnauthorized: if username is None: # If no username is set, set the HTTP Basic Auth challenge # returning an auth realm log.error('No username set in HTTP Authorization header') http_unauthorized = HTTPUnauthorized("No username set") return http_unauthorized(environ, start_response_wrapper) else: # Credentials were set it's just they were invalid raise except HTTPException, e: return e(environ, start_response)
def _complete_login(self, environ, start_response): """Complete the OpenID authentication process. Here we handle the result of the OpenID process. If the process succeeded, we record the username in the session and redirect the user to the page they were trying to view that triggered the login attempt. In the various failures cases we return a 401 Unauthorized response with a brief explanation of what went wrong. """ query = dict(parse_querystring(environ)) # Passing query['openid.return_to'] here is massive cheating, but # given we control the endpoint who cares. response = self._make_consumer(environ).complete( query, query['openid.return_to']) if response.status == SUCCESS: self.log.error('open id response: SUCCESS') sreg_info = SRegResponse.fromSuccessResponse(response) if not sreg_info: self.log.error('sreg_info is None.') exc = HTTPUnauthorized() exc.explanation = ( "You don't have a Launchpad account. Check that you're " "logged in as the right user, or log into Launchpad and try " "again.") raise exc environ[self.session_var]['identity_url'] = response.identity_url environ[self.session_var]['user'] = sreg_info['nickname'] raise HTTPMovedPermanently(query['back_to']) elif response.status == FAILURE: self.log.error('open id response: FAILURE: %s', response.message) exc = HTTPUnauthorized() exc.explanation = response.message raise exc elif response.status == CANCEL: self.log.error('open id response: CANCEL') exc = HTTPUnauthorized() exc.explanation = "Authentication cancelled." raise exc else: self.log.error('open id response: UNKNOWN') exc = HTTPUnauthorized() exc.explanation = "Unknown OpenID response." raise exc
def __call__(self, environ, start_response): request_is_private = (environ['SERVER_PORT'] == str( config.codebrowse.private_port)) environ['loggerhead.static.url'] = environ['SCRIPT_NAME'] if environ['PATH_INFO'].startswith('/static/'): path_info_pop(environ) return static_app(environ, start_response) elif environ['PATH_INFO'] == '/favicon.ico': return favicon_app(environ, start_response) elif environ['PATH_INFO'] == '/robots.txt': return robots_app(environ, start_response) elif not request_is_private: if environ['PATH_INFO'].startswith('/+login'): return self._complete_login(environ, start_response) elif environ['PATH_INFO'].startswith('/+logout'): return self._logout(environ, start_response) path = environ['PATH_INFO'] trailingSlashCount = len(path) - len(path.rstrip('/')) if request_is_private: # Requests on the private port are internal API requests from # something that has already performed security checks. As # such, they get read-only access to everything. identity_url = LAUNCHPAD_SERVICES user = LAUNCHPAD_SERVICES else: identity_url = environ[self.session_var].get( 'identity_url', LAUNCHPAD_ANONYMOUS) user = environ[self.session_var].get('user', LAUNCHPAD_ANONYMOUS) lp_server = get_lp_server(identity_url, branch_transport=self.get_transport()) lp_server.start_server() try: try: branchfs = self.get_branchfs() transport_type, info, trail = branchfs.translatePath( identity_url, urlutils.escape(path)) except xmlrpclib.Fault as f: if check_fault(f, faults.PathTranslationError): raise HTTPNotFound() elif check_fault(f, faults.PermissionDenied): # If we're not allowed to see the branch... if environ['wsgi.url_scheme'] != 'https': # ... the request shouldn't have come in over http, as # requests for private branches over http should be # redirected to https by the dynamic rewrite script we # use (which runs before this code is reached), but # just in case... env_copy = environ.copy() env_copy['wsgi.url_scheme'] = 'https' raise HTTPMovedPermanently(construct_url(env_copy)) elif user != LAUNCHPAD_ANONYMOUS: # ... if the user is already logged in and still can't # see the branch, they lose. exc = HTTPUnauthorized() exc.explanation = "You are logged in as %s." % user raise exc else: # ... otherwise, lets give them a chance to log in # with OpenID. return self._begin_login(environ, start_response) else: raise if transport_type != BRANCH_TRANSPORT: raise HTTPNotFound() trail = urlutils.unescape(trail).encode('utf-8') trail += trailingSlashCount * '/' amount_consumed = len(path) - len(trail) consumed = path[:amount_consumed] branch_name = consumed.strip('/') self.log.info('Using branch: %s', branch_name) if trail and not trail.startswith('/'): trail = '/' + trail environ['PATH_INFO'] = trail environ['SCRIPT_NAME'] += consumed.rstrip('/') branch_url = lp_server.get_url() + branch_name branch_link = urlparse.urljoin(config.codebrowse.launchpad_root, branch_name) cachepath = os.path.join(config.codebrowse.cachepath, branch_name[1:]) if not os.path.isdir(cachepath): os.makedirs(cachepath) self.log.info('branch_url: %s', branch_url) private = info['private'] if private: self.log.info("Branch is private") else: self.log.info("Branch is public") try: bzr_branch = safe_open(lp_server.get_url().strip(':/'), branch_url) except errors.NotBranchError as err: self.log.warning('Not a branch: %s', err) raise HTTPNotFound() bzr_branch.lock_read() try: view = BranchWSGIApp(bzr_branch, branch_name, {'cachepath': cachepath}, self.graph_cache, branch_link=branch_link, served_url=None, private=private) return view.app(environ, start_response) finally: bzr_branch.repository.revisions.clear_cache() bzr_branch.repository.signatures.clear_cache() bzr_branch.repository.inventories.clear_cache() if bzr_branch.repository.chk_bytes is not None: bzr_branch.repository.chk_bytes.clear_cache() bzr_branch.repository.texts.clear_cache() bzr_branch.unlock() finally: lp_server.stop_server()
def __call__(self, environ, start_response): environ['loggerhead.static.url'] = environ['SCRIPT_NAME'] if environ['PATH_INFO'].startswith('/static/'): path_info_pop(environ) return static_app(environ, start_response) elif environ['PATH_INFO'] == '/favicon.ico': return favicon_app(environ, start_response) elif environ['PATH_INFO'] == '/robots.txt': return robots_app(environ, start_response) elif environ['PATH_INFO'].startswith('/+login'): return self._complete_login(environ, start_response) elif environ['PATH_INFO'].startswith('/+logout'): return self._logout(environ, start_response) path = environ['PATH_INFO'] trailingSlashCount = len(path) - len(path.rstrip('/')) user = environ[self.session_var].get('user', LAUNCHPAD_ANONYMOUS) lp_server = get_lp_server(user, branch_transport=self.get_transport()) lp_server.start_server() try: try: transport_type, info, trail = self.branchfs.translatePath( user, urlutils.escape(path)) except xmlrpclib.Fault as f: if check_fault(f, faults.PathTranslationError): raise HTTPNotFound() elif check_fault(f, faults.PermissionDenied): # If we're not allowed to see the branch... if environ['wsgi.url_scheme'] != 'https': # ... the request shouldn't have come in over http, as # requests for private branches over http should be # redirected to https by the dynamic rewrite script we # use (which runs before this code is reached), but # just in case... env_copy = environ.copy() env_copy['wsgi.url_scheme'] = 'https' raise HTTPMovedPermanently(construct_url(env_copy)) elif user != LAUNCHPAD_ANONYMOUS: # ... if the user is already logged in and still can't # see the branch, they lose. exc = HTTPUnauthorized() exc.explanation = "You are logged in as %s." % user raise exc else: # ... otherwise, lets give them a chance to log in # with OpenID. return self._begin_login(environ, start_response) else: raise if transport_type != BRANCH_TRANSPORT: raise HTTPNotFound() trail = urlutils.unescape(trail).encode('utf-8') trail += trailingSlashCount * '/' amount_consumed = len(path) - len(trail) consumed = path[:amount_consumed] branch_name = consumed.strip('/') self.log.info('Using branch: %s', branch_name) if trail and not trail.startswith('/'): trail = '/' + trail environ['PATH_INFO'] = trail environ['SCRIPT_NAME'] += consumed.rstrip('/') branch_url = lp_server.get_url() + branch_name branch_link = urlparse.urljoin( config.codebrowse.launchpad_root, branch_name) cachepath = os.path.join( config.codebrowse.cachepath, branch_name[1:]) if not os.path.isdir(cachepath): os.makedirs(cachepath) self.log.info('branch_url: %s', branch_url) base_api_url = allvhosts.configs['api'].rooturl branch_api_url = '%s/%s/%s' % ( base_api_url, 'devel', branch_name, ) self.log.info('branch_api_url: %s', branch_api_url) req = urllib2.Request(branch_api_url) private = False try: # We need to determine if the branch is private response = urllib2.urlopen(req) except urllib2.HTTPError as response: code = response.getcode() if code in (400, 401, 403, 404): # There are several error codes that imply private data. # 400 (bad request) is a default error code from the API # 401 (unauthorized) should never be returned as the # requests are always from anon. If it is returned # however, the data is certainly private. # 403 (forbidden) is obviously private. # 404 (not found) implies privacy from a private team or # similar situation, which we hide as not existing rather # than mark as forbidden. self.log.info("Branch is private") private = True self.log.info( "Branch state not determined; api error, return code: %s", code) response.close() else: self.log.info("Branch is public") response.close() try: bzr_branch = safe_open( lp_server.get_url().strip(':/'), branch_url) except errors.NotBranchError as err: self.log.warning('Not a branch: %s', err) raise HTTPNotFound() bzr_branch.lock_read() try: view = BranchWSGIApp( bzr_branch, branch_name, {'cachepath': cachepath}, self.graph_cache, branch_link=branch_link, served_url=None, private=private) return view.app(environ, start_response) finally: bzr_branch.repository.revisions.clear_cache() bzr_branch.repository.signatures.clear_cache() bzr_branch.repository.inventories.clear_cache() if bzr_branch.repository.chk_bytes is not None: bzr_branch.repository.chk_bytes.clear_cache() bzr_branch.repository.texts.clear_cache() bzr_branch.unlock() finally: lp_server.stop_server()