def bid_id_signature_verified_active_bid(self): if self.initial_organization == self.test_financial_organization: response = self.app.post_json( '/auctions/{}/bids'.format(self.auction_id), { 'data': { 'tenderers': [self.initial_organization], 'qualified': True, 'eligible': True } }) else: response = self.app.post_json( '/auctions/{}/bids'.format(self.auction_id), { 'data': { 'tenderers': [self.initial_organization], 'qualified': True } }) bidder = response.json['data'] signature = bidder['participationUrl'] before, sep, sig = signature.partition('signature=') sig = b64decode(unquote(str(sig))) signer = Signer( 'fe3b3b5999a08e68dfe62687c2ae147f62712ceace58c1ffca8ea819eabcb5d1'. decode('hex')) ver = Verifier(signer.hex_vk()) verified = ver.verify(sig + str('{}_{}'.format(self.auction_id, bidder['id']))) self.assertEqual(verified, '{}_{}'.format(self.auction_id, bidder['id']))
def _login_jwt_user(self, web_request: WebRequest, create: bool = False) -> Dict[str, Any]: username: str = web_request.get_str('username') password: str = web_request.get_str('password') user_info: Dict[str, Any] if username in RESERVED_USERS: raise self.server.error(f"Invalid Request for user {username}") if create: if username in self.users: raise self.server.error(f"User {username} already exists") salt = secrets.token_bytes(32) hashed_pass = hashlib.pbkdf2_hmac('sha256', password.encode(), salt, HASH_ITER).hex() user_info = { 'username': username, 'password': hashed_pass, 'salt': salt.hex(), 'created_on': time.time() } self.users[username] = user_info action = "user_created" else: if username not in self.users: raise self.server.error(f"Unregistered User: {username}") user_info = self.users[username] salt = bytes.fromhex(user_info['salt']) hashed_pass = hashlib.pbkdf2_hmac('sha256', password.encode(), salt, HASH_ITER).hex() action = "user_logged_in" if hashed_pass != user_info['password']: raise self.server.error("Invalid Password") jwt_secret_hex: Optional[str] = user_info.get('jwt_secret', None) if jwt_secret_hex is None: private_key = Signer() jwk_id = base64url_encode(secrets.token_bytes()).decode() user_info['jwt_secret'] = private_key.hex_seed().decode() user_info['jwk_id'] = jwk_id self.users[username] = user_info self.public_jwks[jwk_id] = self._generate_public_jwk(private_key) else: private_key = self._load_private_key(jwt_secret_hex) jwk_id = user_info['jwk_id'] token = self._generate_jwt(username, jwk_id, private_key) refresh_token = self._generate_jwt( username, jwk_id, private_key, token_type="refresh", exp_time=datetime.timedelta(days=self.login_timeout)) if create: IOLoop.current().call_later(.005, self.server.send_event, "authorization:user_created", {'username': username}) return { 'username': username, 'token': token, 'refresh_token': refresh_token, 'action': action }
def main(global_config, **settings): """ This function returns a Pyramid WSGI application. """ read_users(settings['auth.file']) config = Configurator( settings=settings, authentication_policy=BasicAuthAuthenticationPolicy( auth_check, __name__), authorization_policy=ACLAuthorizationPolicy(), root_factory=Root, ) config.add_request_method(request_params, 'params', reify=True) config.add_subscriber(new_request_subscriber, NewRequest) config.add_subscriber(add_logging_context, ContextFound) config.include('pyramid_exclog') config.add_route('status', '/') config.add_route('register', '/register') config.add_route('upload', '/upload') config.add_route('upload_file', '/upload/{doc_id}') config.add_route('get', '/get/{doc_id}') config.scan(ignore='openprocurement.documentservice.tests') config.registry.signer = signer = Signer( settings.get('dockey', '').decode('hex')) config.registry.dockey = dockey = signer.hex_vk()[:8] verifier = Verifier(signer.hex_vk()) config.registry.dockeyring = dockeyring = {dockey: verifier} dockeys = settings.get( 'dockeys') if 'dockeys' in settings else Signer().hex_vk() for key in dockeys.split('\0'): dockeyring[key[:8]] = Verifier(key) config.registry.keyring = keyring = {dockey: verifier} apikeys = settings.get( 'apikeys') if 'apikeys' in settings else Signer().hex_vk() for key in apikeys.split('\0'): keyring[key[:8]] = Verifier(key) config.registry.apikey = key[:8] config.registry.upload_host = settings.get('upload_host') config.registry.get_host = settings.get('get_host') # search for storage storage = settings.get('storage') for entry_point in iter_entry_points( 'openprocurement.documentservice.plugins', storage): plugin = entry_point.load() config.registry.storage = plugin(config) return config.make_wsgi_app()
def migrate_from0to1(self): set_db_schema_version(self.db, 0) u = Auction(test_auction_data) u.auctionID = "UA-X" u.store(self.db) data = self.db.get(u.id) url = "/tenders/{}/documents/ebcb5dd7f7384b0fbfbed2dc4252fa6e?download=10367238a2964ee18513f209d9b6d1d3" data["documents"] = [{ "id": "ebcb5dd7f7384b0fbfbed2dc4252fa6e", "title": "name.txt", "url": url.format(u.id), "datePublished": "2016-06-01T00:00:00+03:00", "dateModified": "2016-06-01T00:00:00+03:00", "format": "text/plain", }] _id, _rev = self.db.save(data) self.app.app.registry.docservice_url = 'http://localhost' self.app.app.registry.use_docservice = True self.app.app.registry.docservice_key = Signer() migrate_data(self.app.app.registry, 1) migrated_item = self.db.get(u.id) self.assertIn('http://localhost/get/10367238a2964ee18513f209d9b6d1d3?', migrated_item['documents'][0]['url']) self.assertIn('Prefix={}%2Febcb5dd7f7384b0fbfbed2dc4252fa6e'.format(u.id), migrated_item['documents'][0]['url']) self.assertIn('KeyID=', migrated_item['documents'][0]['url']) self.assertIn('Signature=', migrated_item['documents'][0]['url'])
def _load_private_key(self, secret: str) -> Signer: try: key = Signer(bytes.fromhex(secret)) except Exception: raise self.server.error( "Error decoding private key, user data may" " be corrupt", 500) from None return key
def prepare_users_data(tender_data): auction_worker_defaults = BuiltIn().get_variable_value("${auction_worker_defaults}") with open(auction_worker_defaults) as auction_worker_defaults_file: auction_worker_defaults_info = yaml.load(auction_worker_defaults_file) users_data = {} for index, bid in enumerate(tender_data["bids"]): signer = Signer(auction_worker_defaults_info["SIGNATURE_KEY"].decode('hex')) signature = quote(b64encode(signer.signature("{}_{}".format("11111111111111111111111111111111", str(bid['id']))))) users_data[bid["id"]] = { 'login_url': auction_worker_defaults_info['AUCTIONS_URL'].format(auction_id="11111111111111111111111111111111") + '/login?bidder_id={}&signature={}'.format( bid["id"], signature ), 'amount': bid.get('value', {}).get('amount', ''), 'position': positions[index], 'size': size } return users_data
def test_generate_docservice_url(self): request = mock.MagicMock() request.registry = mock.MagicMock() request.registry.docservice_key = Signer( '1234567890abcdef1234567890abcdef') request.registry.docservice_url = 'url' doc_id = '1234567890abcdef1234567890abcdef' expected_result = '/get/1234567890abcdef1234567890abcdef?KeyID=c6c4f29c&Signature=t8L5VW%252BK5vvDwMsxHBhzs%252BcBXFsYAZ%2FM9WJmzgYLVpc8HC9mPbQhsshgGK94XaCtvKFTb9IiTLlW59TM9mV7Bg%253D%253D' result = generate_docservice_url(request, '1234567890abcdef1234567890abcdef', False) self.assertEqual(result, expected_result) expected_result = [ '/get/1234567890abcdef1234567890abcdef?Prefix=test_prefix', '&KeyID=c6c4f29c&Signature=', '&Expires=' ] result = generate_docservice_url(request, '1234567890abcdef1234567890abcdef', True, 'test_prefix') for item in expected_result: self.assertIn(item, result)
def _generate_jwt(self, username: str, jwk_id: str, private_key: Signer, token_type: str = "access", exp_time: datetime.timedelta = JWT_EXP_TIME) -> str: curtime = int(time.time()) payload = { 'iss': self.issuer, 'aud': "Moonraker", 'iat': curtime, 'exp': curtime + int(exp_time.total_seconds()), 'username': username, 'token_type': token_type } header = {'kid': jwk_id} header.update(JWT_HEADER) jwt_header = base64url_encode(json.dumps(header).encode()) jwt_payload = base64url_encode(json.dumps(payload).encode()) jwt_msg = b".".join([jwt_header, jwt_payload]) sig = private_key.signature(jwt_msg) jwt_sig = base64url_encode(sig) return b".".join([jwt_msg, jwt_sig]).decode()
def main(global_config, **settings): config = Configurator( autocommit=True, settings=settings, authentication_policy=AuthenticationPolicy(settings['auth.file'], __name__), authorization_policy=AuthorizationPolicy(), route_prefix=route_prefix(settings), ) config.include('pyramid_exclog') config.include("cornice") config.add_forbidden_view(forbidden) config.add_request_method(request_params, 'params', reify=True) config.add_request_method(authenticated_role, reify=True) config.add_request_method(extract_tender, 'tender', reify=True) config.add_request_method(check_accreditation) config.add_request_method(json_body, 'json_body', reify=True) config.add_renderer('json', JSON(serializer=simplejson.dumps)) config.add_renderer('prettyjson', JSON(indent=4, serializer=simplejson.dumps)) config.add_renderer('jsonp', JSONP(param_name='opt_jsonp', serializer=simplejson.dumps)) config.add_renderer('prettyjsonp', JSONP(indent=4, param_name='opt_jsonp', serializer=simplejson.dumps)) config.add_subscriber(add_logging_context, NewRequest) config.add_subscriber(set_logging_context, ContextFound) config.add_subscriber(set_renderer, NewRequest) config.add_subscriber(beforerender, BeforeRender) config.scan("openprocurement.api.views.spore") config.scan("openprocurement.api.views.health") # tender procurementMethodType plugins support config.add_route_predicate('procurementMethodType', isTender) config.registry.tender_procurementMethodTypes = {} config.add_request_method(tender_from_data) config.add_directive('add_tender_procurementMethodType', register_tender_procurementMethodType) # search for plugins plugins = settings.get('plugins') and settings['plugins'].split(',') for entry_point in iter_entry_points('openprocurement.api.plugins'): if not plugins or entry_point.name in plugins: plugin = entry_point.load() plugin(config) # CouchDB connection db_name = os.environ.get('DB_NAME', settings['couchdb.db_name']) server = Server(settings.get('couchdb.url'), session=Session(retry_delays=range(10))) if 'couchdb.admin_url' not in settings and server.resource.credentials: try: server.version() except Unauthorized: server = Server(extract_credentials(settings.get('couchdb.url'))[0]) config.registry.couchdb_server = server if 'couchdb.admin_url' in settings and server.resource.credentials: aserver = Server(settings.get('couchdb.admin_url'), session=Session(retry_delays=range(10))) config.registry.admin_couchdb_server = aserver users_db = aserver['_users'] if SECURITY != users_db.security: LOGGER.info("Updating users db security", extra={'MESSAGE_ID': 'update_users_security'}) users_db.security = SECURITY username, password = server.resource.credentials user_doc = users_db.get('org.couchdb.user:{}'.format(username), {'_id': 'org.couchdb.user:{}'.format(username)}) if not user_doc.get('derived_key', '') or PBKDF2(password, user_doc.get('salt', ''), user_doc.get('iterations', 10)).hexread(int(len(user_doc.get('derived_key', '')) / 2)) != user_doc.get('derived_key', ''): user_doc.update({ "name": username, "roles": [], "type": "user", "password": password }) LOGGER.info("Updating api db main user", extra={'MESSAGE_ID': 'update_api_main_user'}) users_db.save(user_doc) security_users = [username, ] if 'couchdb.reader_username' in settings and 'couchdb.reader_password' in settings: reader_username = settings.get('couchdb.reader_username') reader = users_db.get('org.couchdb.user:{}'.format(reader_username), {'_id': 'org.couchdb.user:{}'.format(reader_username)}) if not reader.get('derived_key', '') or PBKDF2(settings.get('couchdb.reader_password'), reader.get('salt', ''), reader.get('iterations', 10)).hexread(int(len(reader.get('derived_key', '')) / 2)) != reader.get('derived_key', ''): reader.update({ "name": reader_username, "roles": ['reader'], "type": "user", "password": settings.get('couchdb.reader_password') }) LOGGER.info("Updating api db reader user", extra={'MESSAGE_ID': 'update_api_reader_user'}) users_db.save(reader) security_users.append(reader_username) if db_name not in aserver: aserver.create(db_name) db = aserver[db_name] SECURITY[u'members'][u'names'] = security_users if SECURITY != db.security: LOGGER.info("Updating api db security", extra={'MESSAGE_ID': 'update_api_security'}) db.security = SECURITY auth_doc = db.get(VALIDATE_DOC_ID, {'_id': VALIDATE_DOC_ID}) if auth_doc.get('validate_doc_update') != VALIDATE_DOC_UPDATE % username: auth_doc['validate_doc_update'] = VALIDATE_DOC_UPDATE % username LOGGER.info("Updating api db validate doc", extra={'MESSAGE_ID': 'update_api_validate_doc'}) db.save(auth_doc) # sync couchdb views sync_design(db) db = server[db_name] else: if db_name not in server: server.create(db_name) db = server[db_name] # sync couchdb views sync_design(db) config.registry.db = db # readjust couchdb json decoder couchdb_json_decode() # Document Service key config.registry.docservice_url = settings.get('docservice_url') config.registry.docservice_username = settings.get('docservice_username') config.registry.docservice_password = settings.get('docservice_password') config.registry.docservice_upload_url = settings.get('docservice_upload_url') config.registry.docservice_key = dockey = Signer(settings.get('dockey', '').decode('hex')) config.registry.keyring = keyring = {} dockeys = settings.get('dockeys') if 'dockeys' in settings else dockey.hex_vk() for key in dockeys.split('\0'): keyring[key[:8]] = Verifier(key) # migrate data if not os.environ.get('MIGRATION_SKIP'): for entry_point in iter_entry_points('openprocurement.api.migrations'): plugin = entry_point.load() plugin(config.registry) config.registry.server_id = settings.get('id', '') config.registry.health_threshold = float(settings.get('health_threshold', 99)) config.registry.update_after = asbool(settings.get('update_after', True)) return config.make_wsgi_app()
def main(global_config, **settings): config = Configurator( autocommit=True, settings=settings, authentication_policy=AuthenticationPolicy(settings['auth.file'], __name__), authorization_policy=AuthorizationPolicy(), route_prefix=ROUTE_PREFIX, ) config.include('pyramid_exclog') config.include("cornice") config.add_forbidden_view(forbidden) config.add_request_method(request_params, 'params', reify=True) config.add_request_method(authenticated_role, reify=True) config.add_request_method(check_accreditation) config.add_request_method(json_body, 'json_body', reify=True) config.add_renderer('json', JSON(serializer=simplejson.dumps)) config.add_renderer('prettyjson', JSON(indent=4, serializer=simplejson.dumps)) config.add_renderer( 'jsonp', JSONP(param_name='opt_jsonp', serializer=simplejson.dumps)) config.add_renderer( 'prettyjsonp', JSONP(indent=4, param_name='opt_jsonp', serializer=simplejson.dumps)) # search for plugins plugins = settings.get('plugins') and settings['plugins'].split(',') load_plugins(config, group='openregistry.api.plugins', plugins=plugins) # CouchDB connection aserver, server, db = set_api_security(settings) config.registry.couchdb_server = server if aserver: config.registry.admin_couchdb_server = aserver config.registry.db = db couchdb_json_decode() # Document Service key config.registry.docservice_url = settings.get('docservice_url') config.registry.docservice_username = settings.get('docservice_username') config.registry.docservice_password = settings.get('docservice_password') config.registry.docservice_upload_url = settings.get( 'docservice_upload_url') config.registry.docservice_key = dockey = Signer( settings.get('dockey', '').decode('hex')) config.registry.keyring = keyring = {} dockeys = settings.get( 'dockeys') if 'dockeys' in settings else dockey.hex_vk() for key in dockeys.split('\0'): keyring[key[:8]] = Verifier(key) # migrate data if not os.environ.get('MIGRATION_SKIP'): load_plugins(config.registry, group='openregistry.api.migrations') config.registry.server_id = settings.get('id', '') # search subscribers subscribers_keys = [k for k in settings if k.startswith('subscribers.')] for k in subscribers_keys: subscribers = settings[k].split(',') for subscriber in subscribers: load_plugins(config, group='openregistry.{}'.format(k), name=subscriber) config.registry.health_threshold = float( settings.get('health_threshold', 512)) config.registry.health_threshold_func = settings.get( 'health_threshold_func', 'all') config.registry.update_after = asbool(settings.get('update_after', True)) return config.make_wsgi_app()
def key_decode_sk(string): return Signer(b64_decode(string))
def main(global_config, **settings): dsn = settings.get("sentry.dsn", None) if dsn: LOGGER.info("Init sentry sdk for {}".format(dsn)) sentry_sdk.init( dsn=dsn, integrations=[ LoggingIntegration(level=None, event_level=None), PyramidIntegration() ], send_default_pii=True, request_bodies="always", environment=settings.get("sentry.environment", None), debug=settings.get("sentry.debug", False), ) config = Configurator( autocommit=True, settings=settings, authentication_policy=AuthenticationPolicy(settings["auth.file"], __name__), authorization_policy=AuthorizationPolicy(), route_prefix=ROUTE_PREFIX, ) config.include("pyramid_exclog") config.include("cornice") config.add_forbidden_view(forbidden) config.add_view(precondition, context=HTTPPreconditionFailed) config.add_request_method(request_params, "params", reify=True) config.add_request_method(authenticated_role, reify=True) config.add_request_method(check_accreditations) config.add_request_method(get_currency_rates, name="currency_rates", reify=True) config.add_renderer("json", JSON(serializer=simplejson.dumps)) config.add_renderer("prettyjson", JSON(indent=4, serializer=simplejson.dumps)) config.add_renderer( "jsonp", JSONP(param_name="opt_jsonp", serializer=simplejson.dumps)) config.add_renderer( "prettyjsonp", JSONP(indent=4, param_name="opt_jsonp", serializer=simplejson.dumps)) # search for plugins plugins = settings.get("plugins") and [ plugin.strip() for plugin in settings["plugins"].split(",") ] for entry_point in iter_entry_points("openprocurement.api.plugins"): if not plugins or entry_point.name in plugins: plugin = entry_point.load() plugin(config) # CouchDB connection aserver, server, db = set_api_security(settings) config.registry.couchdb_server = server if aserver: config.registry.admin_couchdb_server = aserver config.registry.db = db # readjust couchdb json decoder couchdb_json_decode() # Document Service key config.registry.docservice_url = settings.get("docservice_url") config.registry.docservice_username = settings.get("docservice_username") config.registry.docservice_password = settings.get("docservice_password") config.registry.docservice_upload_url = settings.get( "docservice_upload_url") config.registry.docservice_key = dockey = Signer( settings.get("dockey", "").decode("hex")) config.registry.keyring = keyring = {} dockeys = settings.get( "dockeys") if "dockeys" in settings else dockey.hex_vk() for key in dockeys.split("\0"): keyring[key[:8]] = Verifier(key) # Archive keys arch_pubkey = settings.get("arch_pubkey", None) config.registry.arch_pubkey = PublicKey( arch_pubkey.decode("hex") if arch_pubkey else SecretKey().pk) # migrate data if not os.environ.get("MIGRATION_SKIP"): for entry_point in iter_entry_points("openprocurement.api.migrations"): plugin = entry_point.load() plugin(config.registry) config.registry.server_id = settings.get("id", "") # search subscribers subscribers_keys = [k for k in settings if k.startswith("subscribers.")] for k in subscribers_keys: subscribers = settings[k].split(",") for subscriber in subscribers: for entry_point in iter_entry_points( "openprocurement.{}".format(k), subscriber): if entry_point: plugin = entry_point.load() plugin(config) config.registry.health_threshold = float( settings.get("health_threshold", 512)) config.registry.health_threshold_func = settings.get( "health_threshold_func", "all") config.registry.update_after = asbool(settings.get("update_after", True)) return config.make_wsgi_app()
async def _login_jwt_user(self, web_request: WebRequest, create: bool = False) -> Dict[str, Any]: username: str = web_request.get_str('username') password: str = web_request.get_str('password') source: str = web_request.get_str('source', self.default_source).lower() if source not in AUTH_SOURCES: raise self.server.error(f"Invalid 'source': {source}") user_info: Dict[str, Any] if username in RESERVED_USERS: raise self.server.error(f"Invalid Request for user {username}") if source == "ldap": if create: raise self.server.error("Cannot Create LDAP User") if self.ldap is None: raise self.server.error("LDAP authentication not available", 401) await self.ldap.authenticate_ldap_user(username, password) if username not in self.users: create = True if create: if username in self.users: raise self.server.error(f"User {username} already exists") salt = secrets.token_bytes(32) hashed_pass = hashlib.pbkdf2_hmac('sha256', password.encode(), salt, HASH_ITER).hex() user_info = { 'username': username, 'password': hashed_pass, 'salt': salt.hex(), 'source': source, 'created_on': time.time() } self.users[username] = user_info self._sync_user(username) action = "user_created" if source == "ldap": # Dont notify user created action = "user_logged_in" create = False else: if username not in self.users: raise self.server.error(f"Unregistered User: {username}") user_info = self.users[username] auth_src = user_info.get("source", "moonraker") if auth_src != source: raise self.server.error( f"Moonraker cannot authenticate user '{username}', must " f"specify source '{auth_src}'", 401) salt = bytes.fromhex(user_info['salt']) hashed_pass = hashlib.pbkdf2_hmac('sha256', password.encode(), salt, HASH_ITER).hex() action = "user_logged_in" if hashed_pass != user_info['password']: raise self.server.error("Invalid Password") jwt_secret_hex: Optional[str] = user_info.get('jwt_secret', None) if jwt_secret_hex is None: private_key = Signer() jwk_id = base64url_encode(secrets.token_bytes()).decode() user_info['jwt_secret'] = private_key.hex_seed().decode() user_info['jwk_id'] = jwk_id self.users[username] = user_info self._sync_user(username) self.public_jwks[jwk_id] = self._generate_public_jwk(private_key) else: private_key = self._load_private_key(jwt_secret_hex) jwk_id = user_info['jwk_id'] token = self._generate_jwt(username, jwk_id, private_key) refresh_token = self._generate_jwt( username, jwk_id, private_key, token_type="refresh", exp_time=datetime.timedelta(days=self.login_timeout)) if create: event_loop = self.server.get_event_loop() event_loop.delay_callback(.005, self.server.send_event, "authorization:user_created", {'username': username}) return { 'username': username, 'token': token, 'source': user_info.get("source", "moonraker"), 'refresh_token': refresh_token, 'action': action }
class AuctionInsiderAuthorizedTest(TaskSet): signer = Signer(SIGNATURE_KEY.decode('hex')) auction_src = None auction_id = None saved_cookies = None last_change = 0 csses = [] dutch_winner = '' dutch_winner_amount = 0 auction_doc = {} ind = False current_phase = None current_stage = None initial_value = 0 current_time = '2000-01-01T00:00:00.000000+02:00' next_stage_start = '2000-01-01T00:00:00+02:00' def __init__(self, parent): self.auction_id = \ auction_id_template.format(random.randint(0, AUCTIONS_NUMBER - 1)) self.bidder_id = BIDDERS[random.randint(0, len(BIDDERS) - 1)] msg = '{}_{}'.format(self.auction_id, self.bidder_id) self.signature = quote(b64encode(self.signer.signature(str(msg)))) self.auth_params = { "bidder_id": self.bidder_id, "signature": self.signature } super(AuctionInsiderAuthorizedTest, self).__init__(parent) def post_bid(self, params): self.client.post( '/insider-auctions/{}/postbid'.format(self.auction_id), data=json.dumps(params), headers={'Content-Type': 'application/json'}, name='Place bid to auction. Phase: {}, id: {}'. format(self.current_phase, self.auction_id) ) @task(1) def main_task(self): self.get_auction_doc_from_couchdb() if self.current_phase != u'announcement': # and self.current_stage >= 0: response = self.client.get( '/insider-auctions/{}/login'.format(self.auction_id), params=self.auth_params, name="Login to auction", allow_redirects=False, catch_response=True ) if response.ok and 'Location' in response.headers: if response.headers['Location'].\ startswith(self.client.base_url): sleep(10) return response = self.client.get(response.headers['Location'], name="Get EULA page") if not response.ok: raise Exception('Client could not get EULA page') redirect_url = urlparse(response.request.url) query = parse_qs(redirect_url.query) params = { "client_id": query['client_id'][0], "scope": query['scope'][0], "bidder_id": query['bidder_id'][0], "response_type": query['response_type'][0], "redirect_uri": query['redirect_uri'][0], "confirm": "yes" } response = self.client.post( '{0.scheme}://{0.netloc}{0.path}'.format(redirect_url), data=params, name="Click yes on EULA" ) if response.ok: self.saved_cookies = copy.copy(self.client.cookies) self.get_auction_page() self.load_all_css() self.load_all_js() self.get_auctions_db_info() long_pool = spawn(self.changes_multiple) self.read_event_source(self.saved_cookies) joinall([long_pool]) else: raise Exception('Client could not click yes on EULA') sleep(10) def get_auction_page(self): resp = self.client.get('/insider-auctions/{}'.format(self.auction_id), name='Get auction page') self.auction_src = resp.content def load_all_css(self): pq = PyQuery(self.auction_src) for style in pq('link[rel="stylesheet"]'): href = style.get('href') if href and href.startswith('/') and not href.startswith('//'): resp = self.client.get(href) if resp.status_code == 200: css = resp.content self.csses.append( tinycss2.parse_stylesheet_bytes( css, skip_comments=True) ) def load_all_js(self): pq = PyQuery(self.auction_src) for script in pq('script'): src = script.get('src') if src and src.startswith('/') and not src.startswith('//'): self.client.get(src) def get_auction_doc_from_couchdb(self): resp = self.client.get( '/database/{0}?_nonce={0}'.format(self.auction_id, random.random()), name="Get document from couch") doc = json.loads(resp.content) self.current_stage = doc['current_stage'] self.current_phase = doc['current_phase'] self.initial_value = doc['initial_value'] if len(doc['stages']) > int(doc['current_stage']) + 1: self.next_stage_start = \ doc['stages'][doc['current_stage'] + 1]['start'] def get_auctions_db_info(self): self.client.get('/database?_nonce={0}'.format(random.random()), name="Get db info") def read_event_source(self, cookies): start_time = time() response = self.client.get( "/insider-auctions/{0}/event_source?lastEventId=&r={1}".format( self.auction_id, random.randint(1000000000000000, 9999999999999999) ), stream=True, cookies=requests.utils.dict_from_cookiejar(cookies), name="Get event_source stream" ) response_length = 0 try: if response.ok: for line in response.iter_lines(): response_length += len(line) sleep(0.1) except: pass total_time = int((time() - start_time) * 1000) events.request_success.fire( request_type="GET", name="Get event_source stream (Finish read)", response_time=total_time, response_length=response_length ) sleep(3) def changes_multiple(self): while self.current_phase != u'announcement': params = {} self.changes() self.get_current_server_time() if self.current_phase == u'dutch' and \ self.auction_doc['current_stage'] >= dutch_steps/2 and \ not self.dutch_winner and \ self.before_time(self.current_time, parse_date(self.next_stage_start)): stage = self.auction_doc['stages'][ self.auction_doc['current_stage']] params['bidder_id'] = self.bidder_id params['bid'] = stage['amount'] elif self.current_phase == u'sealedbid' and \ self.bidder_id != self.dutch_winner and \ self.before_time(self.current_time, parse_date(self.next_stage_start)): params['bidder_id'] = self.bidder_id params['bid'] = random.randint(self.dutch_winner_amount, 99*self.initial_value/100 - 2) elif self.current_phase == u'bestbid' and \ self.bidder_id == self.dutch_winner and \ self.before_time(self.current_time, parse_date(self.next_stage_start)): params['bidder_id'] = self.bidder_id params['bid'] = int(self.initial_value - 1) if params: self.post_bid(params) def get_current_server_time(self): resp = self.client.get( '/get_current_server_time?_nonce={0}'.format(random.random()), name="Get current server time") if resp.status_code == 200: current_time_str = resp.headers['date'] self.current_time = parser.parse(current_time_str) def changes(self): params = { 'timeout': 25000, 'style': 'main_only', 'heartbeat': 10000, 'include_docs': 'true', 'feed': 'longpoll', 'filter': '_doc_ids', 'since': self.last_change, 'limit': 25, '_nonce': random.random(), 'doc_ids': '["{0}"]'.format(self.auction_id) } if self.last_change == 0: name = "Get first change from couch" else: name = "Get change from couch (longpoll)" resp = self.client.get('/database/_changes', params=params, name=name) if resp.status_code == 200: doc = json.loads(resp.content) if len(doc['results']) > 0: self.auction_doc = doc['results'][-1]['doc'] self.current_phase = self.auction_doc['current_phase'] self.current_stage = self.auction_doc['current_stage'] if not self.dutch_winner: for result in self.auction_doc['results']: if 'dutch_winner' in result: self.dutch_winner = result['bidder_id'] self.dutch_winner_amount = result['amount'] self.last_change = doc['last_seq'] @staticmethod def before_time(time1, time2): return time1 < time2 - timedelta(seconds=3)