def test_feedstock_tokens_roundtrip(gh_mock, git_mock, tmp_mock, tmpdir, repo, project): gh_mock.return_value = "abc123" tmp_mock.TemporaryDirectory.return_value.__enter__.return_value = str( tmpdir) user = "******" pth = os.path.expanduser("~/.conda-smithy/foo_%s.token" % project) token_json_pth = os.path.join(tmpdir, "tokens", "%s.json" % project) os.makedirs(os.path.join(tmpdir, "tokens"), exist_ok=True) try: generate_and_write_feedstock_token(user, project) assert os.path.exists(pth) register_feedstock_token(user, project, repo) assert os.path.exists(token_json_pth) with open(pth, "r") as fp: feedstock_token = fp.read().strip() retval = is_valid_feedstock_token(user, project, feedstock_token, repo) finally: if os.path.exists(pth): os.remove(pth) assert retval
async def post(self): headers = self.request.headers feedstock_token = headers.get('FEEDSTOCK_TOKEN', None) data = tornado.escape.json_decode(self.request.body) feedstock = data.get("feedstock", None) LOGGER.info("") LOGGER.info("===================================================") LOGGER.info("token registration for feedstock '%s'" % feedstock) LOGGER.info("===================================================") if ( feedstock_token is None or feedstock is None or not is_valid_feedstock_token( "conda-forge", "staged-recipes", feedstock_token, TOKENS_REPO) ): LOGGER.warning(' invalid token registration request for %s!' % feedstock) self.set_status(403) self.write_error(403) else: register_error = await tornado.ioloop.IOLoop.current().run_in_executor( _thread_pool(), register_feedstock_token_handler, feedstock, ) if register_error: LOGGER.info(' failed token registration request for %s!' % feedstock) self.set_status(403) self.write_error(403) else: LOGGER.info(' token registration request for %s worked!' % feedstock) return
def validate_feedstock_outputs( project, outputs, feedstock_token, register=True, ): """Validate feedstock outputs on the staging channel. Parameters ---------- project : str The name of the feedstock. outputs : dict A dictionary mapping each full qualified output in the conda index to a hash ("md5"), its name ("name"), and version ("version"). feedstock_token : str The secret token used to validate that this feedstock is who it says it is. register : bool If True, attempt to register any outputs that do not exist by pushing the proper json blob to `output_repo`. Default is True. Returns ------- valid : dict A dict keyed on the keys in `outputs` with values True in the output is valid and False otherwise. """ valid = {o: False for o in outputs} if not is_valid_feedstock_token("conda-forge", project, feedstock_token, TOKENS_REPO): return valid, ["invalid feedstock token"] valid_outputs = _is_valid_feedstock_output( project, [o["name"] for _, o in outputs.items()], register=register, ) valid_hashes = _is_valid_output_hash(outputs) errors = [] for o in outputs: _errors = [] if not valid_outputs[outputs[o]["name"]]: _errors.append("output %s not allowed for conda-forge/%s" % (o, project)) if not valid_hashes[o]: _errors.append("output %s does not have a valid md5 checksum" % o) if len(_errors) > 0: errors.extend(_errors) else: valid[o] = True return valid, errors
def test_is_valid_feedstock_token_nofile(gh_mock, git_mock, tmp_mock, tmpdir, repo, project): gh_mock.return_value = "abc123" tmp_mock.TemporaryDirectory.return_value.__enter__.return_value = str( tmpdir) user = "******" feedstock_token = "akdjhfl" retval = is_valid_feedstock_token(user, project, feedstock_token, repo) assert not retval
def test_is_valid_feedstock_token_badtoken(gh_mock, git_mock, tmp_mock, tmpdir, repo, project): gh_mock.return_value = "abc123" tmp_mock.TemporaryDirectory.return_value.__enter__.return_value = str( tmpdir) user = "******" feedstock_token = "akdjhfl" token_pth = os.path.join(tmpdir, "tokens", "%s.json" % project) os.makedirs(os.path.dirname(token_pth), exist_ok=True) with open(token_pth, "w") as fp: json.dump({"salt": b"adf".hex(), "hashed_token": b"fgh".hex()}, fp) retval = is_valid_feedstock_token(user, project, feedstock_token, repo) assert not retval
async def post(self): headers = self.request.headers feedstock_token = headers.get('FEEDSTOCK_TOKEN', None) data = tornado.escape.json_decode(self.request.body) feedstock = data.get("feedstock", None) outputs = data.get("outputs", None) channel = data.get("channel", None) LOGGER.info("") LOGGER.info("===================================================") LOGGER.info("copy outputs for feedstock '%s'" % feedstock) LOGGER.info("===================================================") if ( feedstock_token is None or feedstock is None or outputs is None or channel is None or not is_valid_feedstock_token( "conda-forge", feedstock, feedstock_token, TOKENS_REPO) ): LOGGER.warning(' invalid outputs copy request for %s!' % feedstock) self.set_status(403) self.write_error(403) else: valid, errors = await tornado.ioloop.IOLoop.current().run_in_executor( _thread_pool(), validate_feedstock_outputs, feedstock, outputs, feedstock_token, ) outputs_to_copy = {} for o in valid: if valid[o]: outputs_to_copy[o] = outputs[o] if outputs_to_copy: copied = await tornado.ioloop.IOLoop.current().run_in_executor( _thread_pool(), copy_feedstock_outputs, outputs_to_copy, channel, ) else: copied = {} for o in outputs: if o not in copied: copied[o] = False if not all(v for v in copied.values()): self.set_status(403) self.write(json.dumps({"errors": errors, "valid": valid, "copied": copied})) LOGGER.info(" errors: %s\n valid: %s\n copied: %s" % ( errors, valid, copied)) return
def validate_feedstock_outputs( project, outputs, feedstock_token, ): """Validate feedstock outputs on the staging channel. Parameters ---------- project : str The name of the feedstock. outputs : dict A dictionary mapping each output to its md5 hash. The keys should be the full names with the platform directory, version/build info, and file extension (e.g., `noarch/blah-fa31b0-2020.04.13.15.54.07-py_0.tar.bz2`). feedstock_token : str The secret token used to validate that this feedstock is who it says it is. Returns ------- valid : dict A dict keyed on the keys in `outputs` with values True in the output is valid and False otherwise. errors : list of str A list of any errors encountered. """ valid = {o: False for o in outputs} if not is_valid_feedstock_token( "conda-forge", project, feedstock_token, TOKENS_REPO ): return valid, ["invalid feedstock token"] errors = [] correctly_formatted = {} for o in outputs: try: parse_conda_pkg(o) correctly_formatted[o] = True except RuntimeError: correctly_formatted[o] = False errors.append( "output '%s' is not correctly formatted (it must be the fully " "qualified name w/ extension, `noarch/blah-fa31b0-2020.04.13.15" ".54.07-py_0.tar.bz2`)" % o ) outputs_to_test = {o: v for o, v in outputs.items() if correctly_formatted[o]} valid_outputs = is_valid_feedstock_output( project, outputs_to_test, register=True, ) valid_hashes = _is_valid_output_hash(outputs_to_test) for o in outputs_to_test: _errors = [] if not valid_outputs[o]: _errors.append( "output %s not allowed for conda-forge/%s" % (o, project) ) if not valid_hashes[o]: _errors.append("output %s does not have a valid md5 checksum" % o) if len(_errors) > 0: errors.extend(_errors) else: valid[o] = True return valid, errors