def _determine_target_garden(operation: Operation) -> str: """Determine the system the operation is targeting""" # Certain operations are ASSUMED to be targeted at the local garden if ("READ" in operation.operation_type or "JOB" in operation.operation_type or "FILE" in operation.operation_type or operation.operation_type in ("PLUGIN_LOG_RELOAD", "SYSTEM_CREATE", "SYSTEM_RESCAN") or "PUBLISH_EVENT" in operation.operation_type or "RUNNER" in operation.operation_type or operation.operation_type in ("PLUGIN_LOG_RELOAD", "SYSTEM_CREATE")): return config.get("garden.name") # Otherwise, each operation needs to be "parsed" if operation.operation_type in ("SYSTEM_RELOAD", "SYSTEM_UPDATE"): return _system_id_lookup(operation.args[0]) if operation.operation_type == "SYSTEM_DELETE": # Force deletes get routed to local garden if operation.kwargs.get("force"): return config.get("garden.name") return _system_id_lookup(operation.args[0]) if "INSTANCE" in operation.operation_type: if "system_id" in operation.kwargs and "instance_name" in operation.kwargs: return _system_id_lookup(operation.kwargs["system_id"]) else: return _instance_id_lookup(operation.args[0]) if operation.operation_type == "REQUEST_CREATE": target_system = System( namespace=operation.model.namespace, name=operation.model.system, version=operation.model.system_version, ) return _system_name_lookup(target_system) if operation.operation_type.startswith("REQUEST"): request = db.query_unique(Request, id=operation.args[0]) operation.kwargs["request"] = request return config.get("garden.name") if "GARDEN" in operation.operation_type: if operation.operation_type == "GARDEN_SYNC": sync_target = operation.kwargs.get("sync_target") if sync_target: return sync_target return config.get("garden.name") if operation.operation_type == "QUEUE_DELETE": # Need to deconstruct the queue name parts = operation.args[0].split(".") version = parts[2].replace("-", ".") return _system_name_lookup( System(namespace=parts[0], name=parts[1], version=version)) raise Exception(f"Bad operation type {operation.operation_type}")
def initialize(self): """Actually construct all the various component pieces""" self.scheduler = self._setup_scheduler() load_plugin_log_config() plugin_config = config.get("plugin") self.helper_threads = [ HelperThread( StatusMonitor, timeout_seconds=plugin_config.status_timeout, heartbeat_interval=plugin_config.status_heartbeat, ) ] # Only want to run the MongoPruner if it would do anything tasks, run_every = db.prune_tasks(**config.get("db.ttl")) if run_every: self.helper_threads.append( HelperThread( db.get_pruner(), tasks=tasks, run_every=timedelta(minutes=run_every) ) ) metrics_config = config.get("metrics") if metrics_config.prometheus.enabled: self.helper_threads.append( HelperThread( PrometheusServer, metrics_config.prometheus.host, metrics_config.prometheus.port, ) ) beer_garden.router.forward_processor = QueueListener( action=beer_garden.router.forward, name="forwarder" ) self.mp_manager = self._setup_multiprocessing_manager() beer_garden.local_plugins.manager.lpm_proxy = self.mp_manager.PluginManager() self.entry_manager = beer_garden.api.entry_point.Manager() beer_garden.events.manager = self._setup_events_manager() file_event = Event(name=Events.PLUGIN_LOGGER_FILE_CHANGE.name) self.plugin_log_config_observer = MonitorFile( path=config.get("plugin.logging.config_file"), create_event=file_event, modify_event=file_event, ) self.plugin_local_log_config_observer = MonitorFile( path=config.get("plugin.local.logging.config_file"), create_event=file_event, modify_event=file_event, )
def run(ep_conn): conn_manager = StompManager(ep_conn) _setup_event_handling(conn_manager) _setup_operation_forwarding() entry_config = config.get("entry.stomp") parent_config = config.get("parent.stomp") garden_name = config.get("garden.name") if entry_config.get("enabled"): conn_manager.add_connection(stomp_config=entry_config, name=f"{garden_name}_entry", is_main=True) if parent_config.get("enabled"): conn_manager.add_connection(stomp_config=parent_config, name=f"{garden_name}_parent", is_main=True) for garden in get_gardens(include_local=False): if garden.name != garden_name and garden.connection_type: if garden.connection_type.casefold() == "stomp": connection_params = garden.connection_params.get("stomp", {}) connection_params["send_destination"] = None conn_manager.add_connection(stomp_config=connection_params, name=garden.name) conn_manager.start() logger.info("Stomp entry point started") publish( Event(name=Events.ENTRY_STARTED.name, metadata={"entry_point_type": "STOMP"})) while not shutdown_event.wait(10): for name, info in conn_manager.conn_dict.items(): connection = info.get("conn") if connection: logger.debug(f"{name}: Checking connection") if not connection.is_connected(): logger.debug(f"{name}: Attempting to reconnect") if connection.connect(): logger.debug(f"{name}: Reconnect successful") else: logger.debug(f"{name}: Reconnect failed") conn_manager.shutdown() conn_manager.stop() conn_manager.join(5) logger.debug("Stopping forward processing") beer_garden.router.forward_processor.stop()
def handle_event(event): # Whenever a request is completed check to see if this process is waiting for it if event.name == Events.REQUEST_COMPLETED.name: completion_event = request_map.pop(event.payload.id, None) if completion_event: completion_event.set() # Only care about local garden if event.garden == config.get("garden.name"): if event.name == Events.GARDEN_STOPPED.name: # When shutting down we need to close all handing connections/threads # waiting for a response. This will invoke each connection/thread to be # returned the current status of the Request. for request_event in request_map: request_map[request_event].set() # Only care about downstream garden elif event.garden != config.get("garden.name"): if event.name in ( Events.REQUEST_CREATED.name, Events.REQUEST_STARTED.name, Events.REQUEST_COMPLETED.name, ): # When we send child requests to child gardens where the parent was on # the local garden we remove the parent before sending them. Only setting # the subset of fields that change "corrects" the parent existing_request = db.query_unique(Request, id=event.payload.id) if existing_request: for field in ("status", "output", "error_class"): setattr(existing_request, field, getattr(event.payload, field)) try: db.update(existing_request) except RequestStatusTransitionError: pass else: # Attempt to create the request, if it already exists then continue on try: db.create(event.payload) except NotUniqueException: pass # Required if the main process spawns a wait Request if event.name == Events.REQUEST_COMPLETED.name: if str(event.payload.id) in request_map: request_map[str(event.payload.id)].set()
def ensure_users(): """Create the default admin user if necessary""" if User.objects.count() == 0: username = config.get("auth.default_admin.username") password = config.get("auth.default_admin.password") superuser_role = Role.objects.get(name="superuser") logger.info("Creating default admin user with username: %s", username) admin = User(username=username) admin.set_password(password) admin.role_assignments = [ RoleAssignment(role=superuser_role, domain={"scope": "Global"}) ] admin.save()
def _setup_ssl_context() -> Tuple[Optional[ssl.SSLContext], Optional[ssl.SSLContext]]: http_config = config.get("entry.http") if http_config.ssl.enabled: server_ssl = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) server_ssl.load_cert_chain( certfile=http_config.ssl.public_key, keyfile=http_config.ssl.private_key ) server_ssl.verify_mode = getattr( ssl, "CERT_" + http_config.ssl.client_cert_verify.upper() ) client_ssl = ssl.create_default_context(ssl.Purpose.SERVER_AUTH) client_ssl.load_cert_chain( certfile=http_config.ssl.public_key, keyfile=http_config.ssl.private_key ) if http_config.ssl.ca_cert or http_config.ssl.ca_path: server_ssl.load_verify_locations( cafile=http_config.ssl.ca_cert, capath=http_config.ssl.ca_path ) client_ssl.load_verify_locations( cafile=http_config.ssl.ca_cert, capath=http_config.ssl.ca_path ) else: server_ssl = None client_ssl = None return server_ssl, client_ssl
def default() -> str: """Get the default namespace for this Garden Returns: The default namespace """ return config.get("garden.name")
def _pre_route(operation: Operation) -> Operation: """Called before any routing logic is applied""" # If no source garden is defined set it to the local garden if operation.source_garden_name is None: operation.source_garden_name = config.get("garden.name") if operation.operation_type == "REQUEST_CREATE": if operation.model.namespace is None: operation.model.namespace = config.get("garden.name") elif operation.operation_type == "SYSTEM_READ_ALL": if operation.kwargs.get("filter_params", {}).get("namespace") == "": operation.kwargs["filter_params"]["namespace"] = config.get( "garden.name") return operation
def clean(self): """Validate before saving to the database""" if self.status not in BrewtilsRequest.STATUS_LIST: raise ModelValidationError( f"Can not save Request {self}: Invalid status '{self.status}'") if (self.command_type is not None and self.command_type not in BrewtilsRequest.COMMAND_TYPES): raise ModelValidationError( f"Can not save Request {self}: Invalid command type" f" '{self.command_type}'") if (self.output_type is not None and self.output_type not in BrewtilsRequest.OUTPUT_TYPES): raise ModelValidationError( f"Can not save Request {self}: Invalid output type '{self.output_type}'" ) # Deal with has_parent if self.has_parent is None: self.has_parent = bool(self.parent) elif self.has_parent != bool(self.parent): raise ModelValidationError( f"Cannot save Request {self}: parent value of {self.parent!r} is not " f"consistent with has_parent value of {self.has_parent}") if (self.namespace == config.get("garden.name")) and ( "status" in self.changed_fields or self.created): self.status_updated_at = datetime.datetime.utcnow()
def _setup_application(): """Setup things that can be taken care of before io loop is started""" global io_loop, tornado_app, server, client_ssl io_loop = IOLoop.current() # Set up motor connection moto.create_connection(db_config=beer_garden.config.get("db")) auth_config = config.get("auth") if not auth_config.token_secret: auth_config.token_secret = os.urandom(20) if auth_config.enabled: logger.warning( "Brew-view was started with authentication enabled and no " "Secret. Generated tokens will not be valid across Brew-view " "restarts. To prevent this set the auth.token.secret config.") # This is only used for publishing events for external consumption (in v2 request # events were published with a link to the request, for example). # Commenting this out as it's not useful at the moment # from urllib3.util.url import Url # # http_config = config.get("entry.http") # public_url = Url( # scheme="https" if http_config.ssl.enabled else "http", # host=http_config.public_fqdn, # port=http_config.port, # path=http_config.url_prefix, # ).url tornado_app = _setup_tornado_app() server_ssl, client_ssl = _setup_ssl_context() server = HTTPServer(tornado_app, ssl_options=server_ssl)
def _sync_roles_from_role_definition_file(): """If auth.role_definition_file is set in the main application config, this will load that file and pass it over to sync_roles to make the Role definitions in the database match what is defined in the file. """ role_definition_file: Union[str, None] = config.get("auth.role_definition_file") if role_definition_file: logger.info(f"Syncing role definitions from {role_definition_file}") try: with open(role_definition_file, "r") as filestream: role_definitions = yaml.safe_load(filestream) sync_roles(role_definitions) except FileNotFoundError: raise ConfigurationError( f"Role definition file {role_definition_file} not found.") except SchemaValidationError: raise ConfigurationError( f"Error processing role definition file {role_definition_file}." ) except ValidationError as validation_error: raise ConfigurationError( f"Invalid role definition in {role_definition_file}: {validation_error}" ) else: logger.info( "auth.role_definition_file not defined. No roles will be synced.")
def handle_event(event): # Only care about local garden if event.garden == config.get("garden.name"): if event.name == Events.INSTANCE_INITIALIZED.name: lpm_proxy.handle_initialize(event) elif event.name == Events.INSTANCE_STOPPED.name: lpm_proxy.handle_stopped(event)
def setup_routing(): """Initialize the routing subsystem This will load the cached child garden definitions and use them to populate the two dictionaries that matter, garden_lookup and garden_connections. It will then query the database for all local systems and add those to the dictionaries as well. """ for system in db.query(System, filter_params={"local": True}): add_routing_system(system) # Don't add the local garden for garden in get_gardens(include_local=False): if garden.name != config.get("garden.name"): for system in garden.systems: add_routing_system(system=system, garden_name=garden.name) if (garden.connection_type is not None and garden.connection_type.casefold() != "local"): with garden_lock: gardens[garden.name] = garden if garden.connection_type.casefold() == "stomp": if garden.name not in stomp_garden_connections: stomp_garden_connections[ garden.name] = create_stomp_connection(garden) else: logger.warning( f"Garden with invalid connection info: {garden!r}")
def route(operation: Operation): """Entry point into the routing subsystem Args: operation: The operation to route Returns: """ operation = _pre_route(operation) logger.debug(f"Routing {operation!r}") if not operation.operation_type: raise RoutingRequestException("Missing operation type") if operation.operation_type not in route_functions.keys(): raise RoutingRequestException( f"Unknown operation type '{operation.operation_type}'") # Determine which garden the operation is targeting if not operation.target_garden_name: operation.target_garden_name = _determine_target_garden(operation) if not operation.target_garden_name: raise UnknownGardenException( f"Could not determine the target garden for routing {operation!r}") # If it's targeted at THIS garden, execute if operation.target_garden_name == config.get("garden.name"): return execute_local(operation) else: return initiate_forward(operation)
def _pre_forward(operation: Operation) -> Operation: """Called before forwarding an operation""" # Validate that the operation can be forwarded if operation.operation_type not in routable_operations: raise RoutingRequestException( f"Operation type '{operation.operation_type}' can not be forwarded" ) if operation.operation_type == "REQUEST_CREATE": # Save the request so it'll have an ID and we'll have something to update local_request = create_request(operation.model) if operation.model.namespace == config.get("garden.name"): operation.model = local_request else: # When the target is a remote garden, just capture the id. We don't # want to replace the entire model, as we'd lose the base64 encoded file # parameter data. operation.model.id = local_request.id # Clear parent before forwarding so the child doesn't freak out about an # unknown request operation.model.parent = None operation.model.has_parent = False # Pull out and store the wait event, if it exists wait_event = operation.kwargs.pop("wait_event", None) if wait_event: beer_garden.requests.request_map[operation.model.id] = wait_event return operation
def handle_event(event): """Handle events""" if event.name in (Events.SYSTEM_CREATED.name, Events.SYSTEM_UPDATED.name): add_routing_system(system=event.payload, garden_name=event.garden) elif event.name == Events.SYSTEM_REMOVED.name: remove_routing_system(system=event.payload) # Here we want to handle sync events from immediate children only if (not event.error and (event.name == Events.GARDEN_SYNC.name) and (event.garden != config.get("garden.name")) and (event.garden == event.payload.name)): with routing_lock: # First remove all current routes to this garden remove_routing_garden(garden_name=event.garden) # Then add routes to the new systems for system in event.payload.systems: add_routing_system(system=system, garden_name=event.payload.name) # This is a little unintuitive. We want to let the garden module deal with handling # any downstream garden changes since handling those changes is nontrivial. # It's *those* events we want to act on here, not the "raw" downstream ones. # This is also why we only handle GARDEN_UPDATED and not STARTED or STOPPED if event.garden == config.get("garden.name") and not event.error: if event.name == Events.GARDEN_UPDATED.name: gardens[event.payload.name] = event.payload if event.payload.connection_type: if event.payload.connection_type.casefold() == "stomp": if (event.payload.name in stomp_garden_connections and stomp_garden_connections[ event.payload.name].is_connected()): stomp_garden_connections[ event.payload.name].disconnect() stomp_garden_connections[ event.payload.name] = create_stomp_connection( event.payload) elif event.name == Events.GARDEN_REMOVED.name: try: del gardens[event.payload.name] if event.payload.name in stomp_garden_connections: stomp_garden_connections[event.payload.name].disconnect() del stomp_garden_connections[event.payload.name] except KeyError: pass
def _startup(self): """Initializes core requirements for Application""" self.logger.debug("Starting Application...") self.logger.debug("Starting event manager...") beer_garden.events.manager.start() self.logger.debug("Setting up database...") db.create_connection(db_config=config.get("db")) db.initial_setup(config.get("auth.guest_login_enabled")) self.logger.debug("Setting up message queues...") queue.initial_setup() self.logger.debug("Starting helper threads...") for helper_thread in self.helper_threads: helper_thread.start() self.logger.debug("Setting up garden routing...") beer_garden.router.setup_routing() self.logger.debug("Starting forwarding processor...") beer_garden.router.forward_processor.start() self.logger.debug("Creating and starting entry points...") self.entry_manager.create_all() self.entry_manager.start() self.logger.debug("Starting local plugin process monitoring...") beer_garden.local_plugins.manager.lpm_proxy.start() self.logger.debug("Starting scheduler") self.scheduler.start() self.logger.debug("Publishing startup sync") beer_garden.garden.publish_garden() self.logger.debug("Starting plugin log config file monitors") if config.get("plugin.logging.config_file"): self.plugin_log_config_observer.start() if config.get("plugin.local.logging.config_file"): self.plugin_local_log_config_observer.start() self.logger.debug("Loading jobs from database") self.scheduler.initialize_from_db() self.logger.info("All set! Let me know if you need anything else!")
async def get(self): """Subset of configuration options that the frontend needs""" auth_config = config.get("auth") ui_config = config.get("ui") configs = { "application_name": ui_config.name, "auth_enabled": auth_config.enabled, "icon_default": ui_config.icon_default, "debug_mode": ui_config.debug_mode, "execute_javascript": ui_config.execute_javascript, "garden_name": config.get("garden.name"), "guest_login_enabled": auth_config.guest_login_enabled, "metrics_url": config.get("metrics.prometheus.url"), "url_prefix": config.get("entry.http.url_prefix"), } self.write(configs)
def set_default_headers(self): """Headers set here will be applied to all responses""" self.set_header("BG-Version", beer_garden.__version__) if config.get("ui.cors_enabled"): self.set_header("Access-Control-Allow-Origin", "*") self.set_header("Access-Control-Allow-Headers", "Content-Type") self.set_header("Access-Control-Allow-Methods", "GET, POST, PATCH, DELETE, OPTIONS")
def stop_one( self, runner_id: Optional[str] = None, instance_id: Optional[str] = None, send_sigterm: bool = True, remove: bool = False, # noqa ) -> Runner: """Stop the runner for a given Runner ID or Instance ID. The PluginManager has no ability to places messages on the message queue, so it's possible that a stop message will already have been sent to the plugin that's being asked to stop. If that's NOT the case then send_sigterm should be set to True to attempt to stop the runner gracefully. This will wait for the runner to stop for plugin.local.timeout.shutdown seconds. If the runner is not stopped after that time its process will be killed with SIGKILL. Args: runner_id: The runner ID to stop, optional. instance_id: The instance ID to stop, optional. send_sigterm: If true, send SIGTERM before waiting. Defaults to ``True``. remove: Flag controlling if the runner should be removed from runner list. Defaults to ``False``. Returns: The stopped runner. """ the_runner = None if runner_id is not None: the_runner = self._from_runner_id(runner_id) elif instance_id is not None: the_runner = self._from_instance_id(instance_id) if the_runner is None: raise Exception( f"Could not determine runner using runner ID ({runner_id}) and " f"instance ID ({instance_id})") if send_sigterm: the_runner.term() the_runner.join(config.get("plugin.local.timeout.shutdown")) if the_runner.is_alive(): the_runner.dead = True the_runner.kill() the_runner.stopped = True the_runner.restart = False if remove: self._runners.remove(the_runner) return the_runner.state()
def _environment( self, plugin_config: Dict[str, Any], instance_name: str, plugin_path: Path, runner_id: str, ) -> Dict[str, str]: env = {} # System info comes from config file for key in _SYSTEM_SPEC: key = key.upper() if key in plugin_config: env["BG_" + key] = plugin_config.get(key) env.update( { # Connection info comes from Beer-garden config "BG_HOST": self._connection_info.host, "BG_PORT": self._connection_info.port, "BG_URL_PREFIX": self._connection_info.url_prefix, "BG_SSL_ENABLED": self._connection_info.ssl.enabled, "BG_CA_CERT": self._connection_info.ssl.ca_cert, "BG_CA_VERIFY": False, # TODO - Fix this # The rest "BG_INSTANCE_NAME": instance_name, "BG_RUNNER_ID": runner_id, "BG_PLUGIN_PATH": plugin_path.resolve(), "BG_USERNAME": self._username, "BG_PASSWORD": self._password, } ) if "LOG_LEVEL" in plugin_config: env["BG_LOG_LEVEL"] = plugin_config["LOG_LEVEL"] # Ensure values are all strings for key, value in env.items(): env[key] = json.dumps(value) if isinstance(value, dict) else str(value) # Allowed host env vars for env_var in config.get("plugin.local.host_env_vars"): if env_var in env: logger.warning( f"Skipping host environment variable {env_var} for runner at " f"{plugin_path} as it's already set in the process environment" ) else: env[env_var] = os.environ.get(env_var, default="") # ENVIRONMENT from beer.conf for key, value in plugin_config.get("ENVIRONMENT", {}).items(): env[key] = expand_string(str(value), env) return env
def _setup_events_manager(self): """Set up the event manager for the Main Processor""" event_manager = FanoutProcessor(name="event manager") # Forward all events down into the entry points event_manager.register(self.entry_manager, manage=False) # Register the callback processor event_manager.register(QueueListener(action=garden_callbacks, name="callbacks")) # Set up parent connection cfg = config.get("parent.http") if cfg.enabled: def reconnect_action(): beer_garden.garden.publish_garden(status="RUNNING") easy_client = EasyClient( bg_host=cfg.host, bg_port=cfg.port, bg_url_prefix=cfg.url_prefix, access_token=cfg.access_token, api_version=cfg.api_version, client_timeout=cfg.client_timeout, password=cfg.password, refresh_token=cfg.password, username=cfg.username, ssl_enabled=cfg.ssl.enabled, ca_cert=cfg.ssl.ca_cert, ca_verify=cfg.ssl.ca_verify, client_cert=cfg.ssl.client_cert, client_key=cfg.ssl.client_key, ) event_manager.register( HttpParentUpdater( easy_client=easy_client, black_list=config.get("parent.skip_events"), reconnect_action=reconnect_action, ) ) return event_manager
def test_ensure_local_garden_updates_garden_from_config(self, monkeypatch): """ensure_local_garden should update the name of an existing Garden entry in the database with a connection type of LOCAL""" monkeypatch.setattr(config, "get", self.config_get) Garden(name="thisshouldchange", connection_type="LOCAL").save() ensure_local_garden() garden = Garden.objects.get(connection_type="LOCAL") assert garden.name == config.get("garden.name")
def handle_event(event): """Handle events""" if event.name in (Events.SYSTEM_CREATED.name, Events.SYSTEM_UPDATED.name): add_routing_system(system=event.payload, garden_name=event.garden) elif event.name == Events.SYSTEM_REMOVED.name: remove_routing_system(system=event.payload) # Handle downstream events if event.garden != config.get("garden.name"): if event.name == Events.GARDEN_SYNC.name: # TODO - Do we also need to remove systems here? for system in event.payload.systems: add_routing_system(system=system, garden_name=event.payload.name) # This is a little unintuitive. We want to let the garden module deal with handling # any downstream garden changes since handling those changes is nontrivial. # It's *those* events we want to act on here, not the "raw" downstream ones. # This is also why we only handle GARDEN_UPDATED and not STARTED or STOPPED if event.garden == config.get("garden.name"): if event.name == Events.GARDEN_UPDATED.name: gardens[event.payload.name] = event.payload if event.payload.connection_type: if event.payload.connection_type.casefold() == "stomp": if (event.payload.name in stomp_garden_connections and stomp_garden_connections[ event.payload.name].is_connected()): stomp_garden_connections[ event.payload.name].disconnect() stomp_garden_connections[ event.payload.name] = create_stomp_connection( event.payload) elif event.name == Events.GARDEN_REMOVED.name: try: del gardens[event.payload.name] if event.payload.name in stomp_garden_connections: stomp_garden_connections[event.payload.name].disconnect() del stomp_garden_connections[event.payload.name] except KeyError: pass
def test_ensure_local_garden_creates_new_garden_from_config( self, monkeypatch): """ensure_local_garden should create a Garden entry in the database with name derived from the "garden.name" config setting and a connection type of LOCAL""" monkeypatch.setattr(config, "get", self.config_get) ensure_local_garden() garden = Garden.objects.get(connection_type="LOCAL") assert garden.name == config.get("garden.name")
def __init__(self): handler_config = cast( Box, config.get("auth.authentication_handlers.trusted_header")) self.username_header = handler_config.get("username_header") self.user_groups_header = handler_config.get("user_groups_header") self.create_users = handler_config.get("create_users") self.group_definition_file = cast( str, config.get("auth.group_definition_file")) self.group_mapping = {} if self.group_definition_file: try: self.group_mapping = self._load_group_mapping( self.group_definition_file) except ConfigurationError as exc: logger.error("Error loading group definitions: %s", exc) else: logger.error( "No group_definition_file defined. Users will not be assigned to any " "groups. To fix this, set the 'auth.group_definition_file' " "configuration parameter and restart beer garden.")
def post(self): """ --- summary: Use credentials to generate access and refresh tokens responses: 200: description: All Tokens schema: type: array items: $ref: '#/definitions/Command' 50x: $ref: '#/definitions/50xError' tags: - Tokens """ parsed_body = json.loads(self.request.decoded_body) try: principal = Principal.objects.get(username=parsed_body["username"]) if (config.get("auth.guest_login_enabled") and principal.username == beer_garden.api.http.anonymous_principal.username): verified = True else: verified = yield self.executor.submit( verify, str(parsed_body["password"]), str(principal.hash)) if verified: tokens = generate_tokens(principal, self.REFRESH_COOKIE_EXP) # This is a semi-done solution. To really do this, we cannot give them # a token, instead we should return an error, indicating they need to # update their password, and then login again. In the short term, this # will be enough. This is really meant only to work for our UI so # backwards compatibility is not a concern. if principal.metadata.get( "auto_change" ) and not principal.metadata.get("changed"): self.set_header("change_password_required", "true") if parsed_body.get("remember_me", False): self.set_secure_cookie( self.REFRESH_COOKIE_NAME, tokens["refresh"], expires_days=self.REFRESH_COOKIE_EXP, ) self.write(json.dumps(tokens)) return except DoesNotExist: # Still attempt to verify something so the request takes a while custom_app_context.verify("", None) raise HTTPError(status_code=403, log_message="Bad credentials")
def _verify_db_connection(self): """Verify that that the application can connect to a database Returns: True: the verification was successful False: the app was stopped before a connection could be verified """ self.logger.debug("Verifying database connection...") return self._progressive_backoff( partial(db.check_connection, config.get("db")), "Unable to connect to database, is it started?", )
def _setup_multiprocessing_manager(): BaseManager.register( "PluginManager", callable=partial( PluginManager, plugin_dir=config.get("plugin.local.directory"), log_dir=config.get("plugin.local.log_directory"), connection_info=config.get("entry.http"), username=config.get("plugin.local.auth.username"), password=config.get("plugin.local.auth.password"), ), ) def initializer(): signal.signal(signal.SIGINT, signal.SIG_IGN) signal.signal(signal.SIGTERM, signal.SIG_IGN) data_manager = BaseManager() data_manager.start(initializer=initializer) return data_manager
def test_ensure_roles_creates_roles_defined_in_file( self, role_definition_yaml, config_mock_value): """ensure_roles should create the roles defined in the auth.role_definition_file if specified""" role_definition_file = config.get("auth.role_definition_file") with patch( "builtins.open", mock_open(read_data=role_definition_yaml)) as mock_file_read: ensure_roles() mock_file_read.assert_called_with(role_definition_file, "r") assert len(Role.objects.filter(name="testrole1")) == 1