def connect(self, host, port, username=None, password=None, retry=False): if retry: # Launch a client self.pbClientFactory = ReconnectingPBClientFactory() self.pbClientFactory.gotPerspective = self._connected self.pbClientFactory.disconnected = self._disconnected # Start login if username is None and password is None: self.pbClientFactory.startLogin( Anonymous()) else: self.pbClientFactory.startLogin( UsernamePassword( username, password)) reactor.connectTCP(host, port, self.pbClientFactory) else: # Launch a client self.pbClientFactory = pb.PBClientFactory() reactor.connectTCP(host, port, self.pbClientFactory) yield self.pbClientFactory.getRootObject() if username is None and password is None: yield self.pbClientFactory.login( Anonymous()).addCallback(self._connected) else: yield self.pbClientFactory.login( UsernamePassword( username, password)).addCallback(self._connected)
def getChildWithDefault(self, path, request): """ Inspect the Authorization HTTP header, and return a deferred which, when fired after successful authentication, will return an authorized C{Avatar}. On authentication failure, an C{UnauthorizedResource} will be returned, essentially halting further dispatch on the wrapped resource and all children """ authheader = request.getHeader('authorization') if not authheader: return util.DeferredResource(self._login(Anonymous())) factory, respString = self._selectParseHeader(authheader) if factory is None: return UnauthorizedResource(self._credentialFactories) try: credentials = factory.decode(respString, request) except error.LoginFailed: return UnauthorizedResource(self._credentialFactories) except: log.err(None, "Unexpected failure from credentials factory") return ErrorPage(500, None, None) else: request.postpath.insert(0, request.prepath.pop()) return util.DeferredResource(self._login(credentials))
def requestAvatarId(self, credentials): if IAnonymous.providedBy(credentials): return defer.succeed(Anonymous()) uuid = credentials.username token = credentials.password # lookup key is a hash of the token to prevent timing attacks. # TODO cache the tokens already! db = self._tokens_db() token = db.get(sha512(token).hexdigest()) if token is None: return defer.fail(error.UnauthorizedLogin()) # TODO -- use cryptography constant time builtin comparison. # we compare uuid hashes to avoid possible timing attacks that # might exploit python's builtin comparison operator behaviour, # which fails immediatelly when non-matching bytes are found. couch_uuid_hash = sha512(token[self.TOKENS_USER_ID_KEY]).digest() req_uuid_hash = sha512(uuid).digest() if token[self.TOKENS_TYPE_KEY] != self.TOKENS_TYPE_DEF \ or couch_uuid_hash != req_uuid_hash: return defer.fail(error.UnauthorizedLogin()) return defer.succeed(uuid)
def _authorizedResource(self, request): # check whether the path of the request exists in the app match = self._matchPath(request) if not match: return UnauthorizedResource() # get authorization header or fail header = request.getHeader(b'authorization') if not header: return util.DeferredResource(self._login(Anonymous())) # parse the authorization header auth_data = self._parseHeader(header) if not auth_data: return UnauthorizedResource() # decode the credentials from the parsed header try: credentials = self._credentialFactory.decode(auth_data, request) except error.LoginFailed: return UnauthorizedResource() except: # If you port this to the newer log facility, be aware that # the tests rely on the error to be logged. log.err(None, "Unexpected failure from credentials factory") return ErrorPage(500, None, None) # make sure the uuid given in path corresponds to the one given in # the credentials request_uuid = match.get('uuid') if request_uuid and request_uuid != credentials.username: return ErrorPage(500, None, None) # if all checks pass, try to login with credentials return util.DeferredResource(self._login(credentials))
def getChild(self, path, request): s = request.getSession() if s is None: return request.setupSession() if path == INIT_PERSPECTIVE: def loginSuccess(result): interface, avatarAspect, logout = result s.setResourceForPortal(avatarAspect, self.portal, logout) def triggerLogin(username, password, submit=None): return self.portal.login( UsernamePassword(username, password), None, IResource).addCallback(loginSuccess).addErrback( self._ebFilter) return form.FormProcessor(newLoginSignature.method(triggerLogin), callback=self.callback, errback=self.errback) elif path == DESTROY_PERSPECTIVE: s.portalLogout(self.portal) return Redirect(".") else: r = s.resourceForPortal(self.portal) if r: ## Delegate our getChild to the resource our portal says is the right one. return getResource(r[0], path, request) else: return DeferredResource( self.portal.login(Anonymous(), None, IResource). addCallback( lambda (interface, avatarAspect, logout): getResource( s.setResourceForPortal(avatarAspect, self.portal, logout), path, request)))
def __init__(self, url, version=jsonrpc.VERSION_1, connectTimeout=None, credentials=None, contextFactory=None, pool=None): """ @type url: str @param url: URL of the RPC server. Supports HTTP and HTTPS for now, more might come in the future. @type version: int @param version: Which JSON-RPC version to use? The default is 1.0. @type connectTimeout: float @param connectTimeout: Connection timeout. Note that we don't connect when creating this object, but in callRemote, so the timeout will apply to callRemote. @type credentials: twisted.cred.credentials.ICredentials @param credentials: Credentials for basic HTTP authentication. Supported are Anonymous and UsernamePassword classes. If None then t.c.c.Anonymous object is used as default. @type contextFactory: twisted.internet.ssl.ClientContextFactory @param contextFactory: A context factory for SSL clients. If None then Agent's default is used. @type pool: twisted.web.client.HTTPConnectionPool @param pool: Connection pool used to manage HTTP connections. If None then Agent's default is used. """ self.url = url self.version = version if not credentials: credentials = Anonymous() if not isinstance(credentials, (Anonymous, UsernamePassword)): raise NotImplementedError("'%s' credentials are not supported" % type(credentials)) kwargs = {} if connectTimeout: kwargs['connectTimeout'] = connectTimeout if contextFactory: kwargs['contextFactory'] = contextFactory if pool: kwargs['pool'] = pool self.agent = Agent(reactor, **kwargs) self.credentials = credentials self.auth_headers = None
def connect(self, host, port, username=None, password=None): # Launch a client self.pbClientFactory = pb.PBClientFactory() reactor.connectTCP(host, port, self.pbClientFactory) if username is None and password is None: return self.pbClientFactory.login(Anonymous()).addCallback( self._connected) else: return self.pbClientFactory.login( UsernamePassword(username, password)).addCallback(self._connected)
def test_anonymousError(self): addr = 'http://localhost:%s' % self.portNumber proxy = Proxy(addr, credentials=Anonymous()) d = proxy.callRemote('echo', '') e = self.assertFailure(d, jsonrpc.JSONRPCError) def finished(result): self.assertEquals(result.strerror, 'Unauthorized') self.assertEquals(result.errno, jsonrpc.INVALID_REQUEST) e.addCallback(finished) return d
def test_anonymousLogin(self): data = 'some random string' addr = 'http://localhost:%s' % self.portNumber proxy = Proxy(addr, jsonrpc.VERSION_1, credentials=Anonymous()) d = proxy.callRemote('echo', data) def finished(result): self.assertEquals(result, data) d.addCallback(finished) return d
def requestAvatarId(self, credentials): if IAnonymous.providedBy(credentials): return defer.succeed(Anonymous()) service = credentials.username token = credentials.password # TODO: Use constant time comparison if self._trusted_services_tokens[service] != token: return defer.fail(error.UnauthorizedLogin()) return defer.succeed(service)
def test_sessionCleanup(self): """ Expired sessions are cleaned up every C{sessionCleanFrequency} seconds. """ clock = Clock() store = Store() portal = Portal(_TrivialRealm()) portal.registerChecker(AllowAnonymousAccess(), IAnonymous) request = FakeRequest(headers={'host': 'example.com'}) resource = PersistentSessionWrapper( store, portal, domains=['example.org', 'example.com'], clock=clock) session = GuardSession(resource, b'uid') # Create two sessions resource.createSessionForKey(b'key1', b'username@domain') resource.createSessionForKey(b'key2', b'username@domain') self.assertEqual(store.query(PersistentSession).count(), 2) # Session shouldn't be cleaned yet resource.login(request, session, Anonymous(), ()) self.assertEqual(store.query(PersistentSession).count(), 2) # First session is expired and it's time for a clean ps = store.findUnique(PersistentSession, PersistentSession.sessionKey == b'key1') ps.lastUsed -= timedelta(seconds=PERSISTENT_SESSION_LIFETIME + 1) clock.advance(SESSION_CLEAN_FREQUENCY + 1) resource.login(request, session, Anonymous(), ()) self.assertEqual( list(store.query(PersistentSession).getColumn('sessionKey')), [b'key2']) # Now we expire the second session ps2 = store.findUnique(PersistentSession, PersistentSession.sessionKey == b'key2') ps2.lastUsed -= timedelta(seconds=PERSISTENT_SESSION_LIFETIME + 1) clock.advance(SESSION_CLEAN_FREQUENCY + 1) resource.login(request, session, Anonymous(), ()) self.assertEqual(store.query(PersistentSession).count(), 0)
def remote_loginAnonymous(self, mind): """ Attempt an anonymous login. @param mind: An object to use as the mind parameter to the portal login call (possibly None). @rtype: L{Deferred} @return: A Deferred which will be called back with an avatar when login succeeds or which will be errbacked if login fails somehow. """ d = self.portal.login(Anonymous(), mind, IPerspective) d.addCallback(self._cbLogin) return d
def xmlrpc_authenticate(self, username, password): """server authentication method""" print "MaayRPCServer xmlrpc_authenticate", username # anonymous login if (username, password) == ('', ''): creds = Anonymous() onSuccess = lambda d,u,p: (ANONYMOUS_AVATARID, '') else: creds = UsernamePassword(username, password) onSuccess = self._attachUser d = self.portal.login(creds, None, IQuerier) d.addCallback(onSuccess, username, password) # self._attachUser, username, password) d.addErrback(lambda failure: ('', str(failure))) return d
def _get_or_create_session(self, provider, full_id, password=""): if full_id in self._sessions: return self._sessions[full_id] if full_id == ANONYMOUS: credentials = Anonymous() provider_id = provider.domain else: username, provider_id = config.get_username_and_provider(full_id) credentials = UsernamePassword(username, password) api = self._get_api(provider) provider_pem = config.get_ca_cert_path(_preffix, provider_id) session = Session(credentials, api, provider_pem) self._sessions[full_id] = session return session
def _authorizedResource(self, request): # check whether the path of the request exists in the app match = self._matchPath(request) if not match: return UnauthorizedResource() # get authorization header or fail header = request.getHeader(b'authorization') if not header: return util.DeferredResource(self._login(Anonymous())) # parse the authorization header auth_data = self._parseHeader(header) if not auth_data: return UnauthorizedResource() # decode the credentials from the parsed header try: credentials = self._credentialFactory.decode(auth_data, request) except error.LoginFailed: return UnauthorizedResource() except Exception: # If you port this to the newer log facility, be aware that # the tests rely on the error to be logged. log.err(None, "Unexpected failure from credentials factory") return ErrorPage(500, None, None) # make sure the uuid given in path corresponds to the one given in # the credentials request_uuid = match.get('uuid') if request_uuid and request_uuid != credentials.username: return ErrorPage(500, None, None) # eventually return a cached resouce sessionData = _sessionData(request) if sessionData.username == credentials.username \ and sessionData.password == credentials.password: return self._portal.realm.auth_resource # if all checks pass, try to login with credentials and cache # credentials in case of success def _cacheSessionData(res): sessionData.username = credentials.username sessionData.password = credentials.password return res d = self._login(credentials) d.addCallback(_cacheSessionData) return util.DeferredResource(d)
def _authorizedResource(self, request): """ Get the L{IResource} which the given request is authorized to receive. If the proper authorization headers are present, the resource will be requested from the portal. If not, an anonymous login attempt will be made. """ cert = request.channel.transport.getPeerCertificate() # Anonymous access if no certificate if not cert: return webutil.DeferredResource(self._login(Anonymous())) # TODO hard-coded to Kontalk usage return webutil.DeferredResource(self._login(self._credential(cert)))
def _get_config_for_all_services(self, session): if session is None: provider_cert = self._get_ca_cert_path() session = Session(Anonymous(), self.api_uri, provider_cert) services_dict = self._load_provider_configs() configs_path = self._get_configs_path() with open(configs_path) as jsonf: services_dict = Record(**json.load(jsonf)).services pending = [] base = self._disco.get_base_uri() for service in self._provider_config.services: if service in self.SERVICES_MAP.keys(): for subservice in self.SERVICES_MAP[service]: uri = base + str(services_dict[subservice]) path = self._get_service_config_path(subservice) d = session.fetch_provider_configs(uri, path, method='GET') pending.append(d) return defer.gatherResults(pending)
def _reconnect(self): try: if self._client: self._client.disconnect() self._client = PBClientFactory() if self._listener: self._listener.disconnect() self._listener = reactor.connectTCP(self.forwardserver, self.forwardport, self._client) self._remote = self._client.login(Anonymous()) self._remote.addCallback(self._login) self._remote.addErrback(self._loginFailed) except Exception, e: logger.error( "[output:%s] failed to connect to remote collector: %s" % (self.name, str(e))) logger.error("[output:%s] will retry to connect in %i seconds" % (self.name, self.retryinterval)) self._backoff = reactor.callLater(self.retryinterval, self._reconnect)
def main(): """ Connect to a PB server running on port 8800 on localhost and log in to it, both anonymously and using a username/password it will recognize. """ startLogging(stdout) factory = PBClientFactory() reactor.connectTCP("localhost", 8800, factory) anonymousLogin = factory.login(Anonymous()) anonymousLogin.addCallback(connected) anonymousLogin.addErrback(error, "Anonymous login failed") usernameLogin = factory.login(UsernamePassword("user1", "pass1")) usernameLogin.addCallback(connected) usernameLogin.addErrback(error, "Username/password login failed") bothDeferreds = gatherResults([anonymousLogin, usernameLogin]) bothDeferreds.addCallback(finished) reactor.run()
def __init__(self, url, version=jsonrpc.VERSION_1, connectTimeout=None, credentials=Anonymous(), contextFactory=WebClientContextFactory()): """ @type url: str @param url: URL of the RPC server. Supports HTTP and HTTPS for now, more might come in the future. @type version: int @param version: Which JSON-RPC version to use? The default is 1.0. @type connectTimeout: float @param connectTimeout: Connection timeout. Note that we don't connect when creating this object, but in callRemote, so the timeout will apply to callRemote. @type credentials: twisted.cred.credentials.ICredentials @param credentials: Credentials for basic HTTP authentication. Supported are Anonymous and UsernamePassword classes. @type contextFactory: twisted.internet.ssl.ClientContextFactory @param contextFactory: A context factory for SSL clients. """ self.url = url self.version = version if not isinstance(credentials, (Anonymous, UsernamePassword)): raise NotImplementedError("'%s' credentials are not supported" % type(credentials)) self.agent = Agent(reactor, connectTimeout=connectTimeout, contextFactory=contextFactory) self.credentials = credentials self.auth_headers = None
def _authorizedResource(self, request): """ Get the L{IResource} which the given request is authorized to receive. If the proper authorization headers are present, the resource will be requested from the portal. If not, an anonymous login attempt will be made. """ authheader = request.getHeader(b'authorization') if not authheader: return util.DeferredResource(self._login(Anonymous())) factory, respString = self._selectParseHeader(authheader) if factory is None: return UnauthorizedResource(self._credentialFactories) try: credentials = factory.decode(respString, request) except error.LoginFailed: return UnauthorizedResource(self._credentialFactories) except: self._log.failure("Unexpected failure from credentials factory") return ErrorPage(500, None, None) else: return util.DeferredResource(self._login(credentials))
def _authorizedResource(self, request): """ Get the L{IResource} which the given request is authorized to receive. If the proper authorization """ sess = request.getSession() if sess.uid not in self.sessions: # New Session self.sessions.add(sess.uid) sess.notifyOnExpire(lambda: self._expired(sess)) # Session is authenticated already if sess.is_authed(): return util.DeferredResource( self._login(SessionCookie(request), sess)) # Perform an Anonymous login if not self.check_credentials(request): return util.DeferredResource(self._login(Anonymous(), sess)) # Try to authenticate the session return util.DeferredResource( self._login(self.get_credentials(request), sess))
def render_POST(self, request): params = get_unicode_params(request) action = params.get(u"action", [u"login"])[0] if action == u"prepare": # prepare client token token = params.get(u"token", [None])[0] secret = params.get(u"secret", [None])[0] if (token is None) and (secret is None): # anonymous website credentials = Anonymous() elif (token is None) or (secret is None): # one value is missing, invalid request defer.returnValue(u'{"status": "error", "reason": "token OR secret missing"}') else: # token / secret provided credentials = UnicodeUsernamePassword(token, secret) try: _, perm, logout = yield self._token_portal.login(credentials, None, IWebAuthPermission) except (Unauthorized, LoginFailed): defer.returnValue(u'{"status": "error", "reason": "token/secret invalid"}') else: # this is not an interactive season, so call logout immediately logout() ctoken = self._new_ctoken() self._tokens[ctoken] = (self._TOKEN_STATE_LOGIN, perm, None) defer.returnValue(u'{"status": "success", "client_token": "' + ctoken + '"}') elif action == u"login": # login credentials params = get_unicode_params(request) ctoken = params.get("ctoken", [None])[0] if ctoken not in self._tokens: # invalid client token html = (yield self._login_resource.render_invalid_login(request)) or u"Error: Invalid ctoken" defer.returnValue(html) credentials = self._login_resource.handle_login_response(params) # attempt login try: _, userdata, logout = yield self._user_portal.login(credentials, None, IUserData) except (Unauthorized, LoginFailed): # invalid login response = (yield self._login_resource.render_login_failed(request)) or u"Login failed" defer.returnValue(response) else: # login successfull # first off, call logout as twisted cred wants logout() # secondly, set the client login cookie session = request.getSession() cookie = ILoginCookie(session) cookie.userdata = userdata # thirdly, prepare the userdata _, perm, _2 = self._tokens[ctoken] self._tokens[ctoken] = (self._TOKEN_STATE_CB, perm, userdata) # finally, redirect to callback defer.returnValue( redirectTo( u"{}?action=callback&ctoken={}".format( perm.callback_url, ctoken, ).encode(_URL_ENCODING), request, ) ) else: # unknown action request.setResponseCode(404) defer.returnValue(u"Error: Invalid action specified.")
def checkLogin(self, ctx, session, segments, sessionURL=None, httpAuthCredentials=None): """ Associate the given request with the given session and: - log the user in to our portal, if they are accessing a login URL - log the user out from our portal (calling their logout callback), if they are logged in and accessing a logout URL - Move the request parameters saved on the session, if there are any, onto the request if a session just started or a login just succeeded. @return: - if the user is already logged in: a 2-tuple of requestObject, C{segments} (i.e. the segments parameter) - if the user is not logged in and not logging in, call login() to initialize an anonymous session, and return a 2-tuple of (rootResource, segments-parameter) from that anonymous session. This counts as logging in for the purpose of future calls to checkLogin. - if the user is accessing a login URL: a 2-tuple of the logged in resource object root and the remainder of the segments (i.e. the URL minus __login__) to be passed to that resource. """ request = inevow.IRequest(ctx) session.touch() request.session = session root = url.URL.fromContext(request) if sessionURL is not None: root = root.child(sessionURL) request.rememberRootURL(str(root)) spoof = False if getattr(session, 'sessionJustStarted', False): del session.sessionJustStarted spoof = True if getattr(session, 'justLoggedIn', False): del session.justLoggedIn spoof = True if spoof and hasattr(session, 'args'): request.args = session.args request.fields = session.fields request.content = StringIO.StringIO() request.content.close() request.method = session.method request.received_headers = session.received_headers del session.args, session.fields, session.method, session.received_headers if segments and segments[0] in (LOGIN_AVATAR, LOGOUT_AVATAR): authCommand = segments[0] else: authCommand = None if httpAuthCredentials: # This is the FIRST TIME we have hit an HTTP auth session with our # credentials. We are going to perform login. assert not authCommand, ( "HTTP auth support isn't that robust. " "Come up with something to do that makes sense here.") return self.login(request, session, httpAuthCredentials, segments).addErrback(self.authRequiredError, session) if authCommand == LOGIN_AVATAR: subSegments = segments[1:] def unmangleURL((res, segs)): # Tell the session that we just logged in so that it will # remember form values for us. session.justLoggedIn = True # Then, generate a redirect back to where we're supposed to be # by looking at the root of the site and calculating the path # down from there using the segments we were passed. u = url.URL.fromString(request.getRootURL()) for seg in subSegments: u = u.child(seg) return u, () return self.login(request, session, self.getCredentials(request), subSegments).addCallback(unmangleURL).addErrback( self.incorrectLoginError, ctx, subSegments, "Incorrect login.") elif authCommand == LOGOUT_AVATAR: self.explicitLogout(session) return urlToChild(ctx, *segments[1:]), () else: r = session.resourceForPortal(self.portal) if r: ## Delegate our getChild to the resource our portal says is the right one. return r[0], segments else: # XXX I don't think that the errback here will work at all, # because the redirect loop would be infinite. Perhaps this # should be closer to the HTTP auth path? return self.login(request, session, Anonymous(), segments).addErrback( self.incorrectLoginError, ctx, segments, 'Anonymous access not allowed.')