diff --git a/iam/app.py b/iam/app.py index 752c439..7e2f90b 100644 --- a/iam/app.py +++ b/iam/app.py @@ -1,6 +1,7 @@ import glob import logging import os +import sys from collections import namedtuple from copy import copy from pprint import pprint @@ -12,23 +13,24 @@ from . import exceptions as error from .catalog import Catalog from .framework import DictItem, scoped_ident from .idents import Idents -from .lib.utils import get_root_pkg_dir, import_module, open_yaml +from .lib.utils import (filter_existing_files, get_root_pkg_dir, import_module, + open_yaml, to_yaml) from .meta import NAME, VERSION from .providers import Providers logger = logging.getLogger(__name__) -IamException = error.IamException +# IamException = error.IamException class App: name = NAME - def __init__(self, ident="_", config_path=None): - # Load application - db = self.load_config_files(config_path=config_path) + def __init__(self, ident="_", config_path=None, cli_context=None): + self.cli_ctx = cli_context - self.load_db(db) + # Load application + self.init_db(config_path) self.init_ident(ident) def init_ident(self, ident): @@ -49,7 +51,7 @@ class App: self.ident = self.idents.get(self.ident_name) # Load user and catalog - self.catalog = Catalog(self, self.ident) + self.catalog = Catalog(self, self.ident, self.cli_ctx) def load_config_files(self, config_path=None): "Fetch config from default place" @@ -68,7 +70,7 @@ class App: files = glob.glob(f"{path}/*.yml") for file_ in files: - file_ = os.path.join(path, file_) + # file_ = os.path.join(path, file_) # Python > 3.10, see above payloads = open_yaml(file_) for payload in payloads: config_candidates.append( @@ -89,16 +91,24 @@ class App: return config - def load_db(self, db): + def init_db(self, config_path): "Load database" + # Update python PATH for later plugin loading + self.conf_plugin_dir = os.path.join(config_path, "plugins") + sys.path.insert(0, self.conf_plugin_dir) + # Init Databases + db = self.load_config_files(config_path=config_path) self.db = db self._load_plugins(db.get("plugins", [])) # Load providers providers_conf = {} providers_conf.update(self.plugin_providers) + + # pprint(providers_conf) + providers_conf.update(db.get("providers", {})) self.providers = Providers("ConfigProviders", providers_conf) @@ -114,29 +124,52 @@ class App: plugins = {} for plugin_name in plugins_refs: - if not ":" in plugin_name: - plugin_name = plugin_name + ":all" + providers = self.parse_plugin_def(plugin_name) - load_one = False - if not plugin_name.endswith(":all"): - load_one = plugin_name.split(":", 2)[1] + plugins.update(providers) - mod = None - try: - mod = import_module(plugin_name) - except ModuleNotFoundError as err: - msg = f"Impossible to load module: {plugin_name}" - raise IamException(msg) - print(err) - continue - - if load_one: - mod = {load_one: mod} - - plugins.update(mod) + # print (to_yaml(mod)) self.plugin_providers = plugins + def parse_plugin_def(self, plugin_name): + mod_name = plugin_name + mod_comp = "__all__" + if ":" in plugin_name: + mod_name, mod_comp = plugin_name.split(":", 2) + plugin_name = f"{mod_name}:{mod_comp}" + + # Load plugin + providers = None + matches = filter_existing_files( + self.conf_plugin_dir, [f"{mod_name}.yml", f"{mod_name}.yaml"] + ) + + if matches: + logger.debug(f"Load YAML plugin {mod_name}") + providers = open_yaml(matches[0])[0].get("providers") + else: + try: + mod = import_module(mod_name) + logger.debug(f"Load Python plugin {mod_name}") + providers = mod.providers + except ModuleNotFoundError: + pass + + # pprint(providers) + + # Report empty plugins + if not providers: + _msg = f"Impossible to load module: {plugin_name}" + raise error.ConfigUnknownPlugin(_msg) + + # Select only required items + if not plugin_name.endswith(":__all__"): + item = providers.get(mod_comp) + providers = {mod_comp: item} + + return providers + def user_dump(self, pattern=None): "Dump ident config" diff --git a/iam/catalog.py b/iam/catalog.py index 15e7290..baa6a3b 100644 --- a/iam/catalog.py +++ b/iam/catalog.py @@ -11,8 +11,8 @@ import sh from . import exceptions as error from .framework import DictCtrl, DictItem, KeyValue, KeyValueExtra from .idents import Ident -from .lib.utils import (empty, format_render, jinja_render, - jinja_template_vars, uniq) +from .lib.utils import (duplicates, empty, format_render, import_module, + jinja_render, jinja_template_vars, uniq) logger = logging.getLogger(__name__) cli_logger = logging.getLogger("iam.cli") @@ -55,6 +55,7 @@ class ServiceCommand(DictItem): default_attrs = { "desc": "Service without description", "cmd": "", # Direct commands to launch + "python": "", # Will be run in as python "shell": "", # Will be run in a shell (or specific shell below) # "shell_sh": "", # "shell_bash": "", @@ -301,7 +302,7 @@ class Services(DictCtrl): # Retrieve command list cmds = self.list_cmds() - cmd_names = [cmd.name for cmd in cmds] + cmd_names = [cmd.cmd.name for cmd in cmds] # Find best matching command cmd_split_idx = None @@ -328,26 +329,28 @@ class Services(DictCtrl): def list_cmds(self): "List all services commands" - ret = [] + cmd_catalog = [] for name, service in self.items(): cmds = service.get_cmds() - ret.extend(cmds) - # Check for invalid configs - conf = [] - for cmd in ret: - prefix = cmd.name.split(" ")[0] - if prefix in self.RESERVED_CMD_PREFIX: - _msg = f"Forbidden prefix! {cmd}" - raise Exception(_msg) - conf.append(cmd.name) - dump = uniq(conf) + for cmd in cmds: + # Check for invalid prefixes + prefix = cmd.name.split(" ")[0] + if prefix in self.RESERVED_CMD_PREFIX: + _msg = f"Forbidden prefix for command '{cmd.name}' in service: '{service.name}'" + raise error.ConfigForbiddenPrefixCmds(_msg) - if conf != dump: - _msg = f"Duplicates values! {conf}" - raise Exception(_msg) + # Check for duplicates + dups = [x for x in cmd_catalog if x.cmd.name == cmd.name] + if dups: + svcs = ",".join([x.svc.name for x in dups]) + _msg = f"Duplicate command name '{cmd.name}' for services: '{svcs}' and '{service.name}'" + raise error.ConfigDuplicateCmds(_msg) - return ret + # Append to catalog + cmd_catalog.append(SimpleNamespace(svc=service, cmd=cmd)) + + return cmd_catalog def get_linked_resources(self): """ @@ -763,6 +766,7 @@ class Context(DictItem): default_attrs = { "ident": None, + "cli_ctx": {}, } # Overrides @@ -795,7 +799,7 @@ class Context(DictItem): class Catalog: "Manage catalog resources" - def __init__(self, app, ident): + def __init__(self, app, ident, cli_ctx=None): # Prepare catalog self.app = app self.ident = ident @@ -803,6 +807,7 @@ class Catalog: # Prepare context ctx_conf = { "ident": ident, + "cli_ctx": cli_ctx, } self.context = Context("current_context", ctx_conf, parent=self) @@ -847,9 +852,11 @@ class Catalog: # Get resources from providers and user providers = self.app.providers ident = self.ident + # pprint (self.__dict__) # Build kind catalog res_kind_configs = providers.get_resource_kinds() + assert res_kind_configs, "TOFIX" self.resources_kind = ResourcesKinds( "CatalogResourcesKinds", payload=res_kind_configs, parent=self ) @@ -986,12 +993,13 @@ class Catalog: def run_svc_cmd(self, cmd): "Run a command against the services" - cmd, args = self.services.get_svc_command(cmd) + cmd_def, args = self.services.get_svc_command(cmd) - # pprint (cmd.cmd) + # pprint (cmd_def) # pprint (args) - service = cmd.service + service = cmd_def.svc + cmd = cmd_def.cmd # pprint (service) # pprint (service.__dict__) @@ -1019,15 +1027,17 @@ class Catalog: # pprint (ctx_vars) + env_prefix = "IAM_" new_env = dict(os.environ) - new_env.update(ctx_vars) - # pprint(new_env) + new_env.update({env_prefix + key: val for key, val in ctx_vars.items()}) - if cmd.cmd and cmd.shell: - logger.warning(f"Duplicate cmd and shell for {service.name}") + # Validate config: TOFIX: Should not be here ! + tests = [cmd.cmd, cmd.shell, cmd.python] + tests = [x for x in tests if x] + if len(tests) > 1: + logger.warning(f"Duplicate cmd and shell for {service.name}: {tests}") out = None - if cmd.shell: mode = "shell" payload = cmd.shell @@ -1069,9 +1079,12 @@ class Catalog: payload = cmd.cmd real_cmd = jinja_render(payload, ctx_vars) - cmd_parts = real_cmd.split(" ", 1) + # print ("PAYLOAD", real_cmd) + + cmd_parts = real_cmd.split(" ") sh_cmd = cmd_parts[0] - sh_args = cmd_parts[1] if len(cmd_parts) > 0 else [] + sh_args = cmd_parts[1:] if len(cmd_parts) > 1 else [] + sh_args.extend(args) # print ("Prepare", sh_cmd, " ||| ", sh_args) @@ -1080,11 +1093,41 @@ class Catalog: cmd = sh.Command(sh_cmd) if sh_args: + # sh_args = sh_args.split(" ") + pprint(cmd) + pprint(sh_args) out = cmd(sh_args, _fg=True, _env=new_env) else: out = cmd(_fg=True, _env=new_env) # print (out) + elif cmd.python: + mode = "python" + plugin_name = cmd.python + # print ("LOAD MODULE", plugin_name) + + # this expect a callable, function or class + try: + mod = import_module(plugin_name) + except ModuleNotFoundError as err: + msg = f"Impossible to load module: {plugin_name}" + raise IamException(msg) + print(err) + + cmd_name = cmd.name.replace(" ", "_") + + # pprint (mod) + # print ("locals") + # pprint (locals()) + + # print ("RUN PLUGIN") + mod( + cmd_name=cmd_name, + env=new_env, + vars=ctx_vars, + args=args, + ctx=self.context, + ) else: raise Exception("MIssing cmd or shell in config !") diff --git a/iam/cli_click.py b/iam/cli_click.py index 3547b01..c0066ee 100755 --- a/iam/cli_click.py +++ b/iam/cli_click.py @@ -4,6 +4,7 @@ import io import logging import os import sys +import traceback from enum import Enum from pprint import pprint from types import SimpleNamespace @@ -45,7 +46,7 @@ else: import rich_click as click from rich_click import RichGroup - from rich import box # , print + from rich import box, print from rich.console import Console # Overrides defaults from rich.pretty import pprint @@ -90,7 +91,8 @@ list_view = ViewList(output=console.print) APP_NAME = "iam" APP_VERSION = "__version__TOFIX" APP_EXCEPTION = error.IamException - +APP_TTY = os.isatty(0) +APP_COLORS = APP_TTY or APP_RICH # See: https://docs.python.org/3.8/library/logging.html#logging-levels DEFAULT_LOG_LEVEL = 30 # warning @@ -129,16 +131,37 @@ def global_options(function): # is_flag=True, show_default=False, default=False, # help="Force" # )(function) - # function = click.option( - # "interactive", "--interactive", "-i" - # is_flag=True, show_default=False, default=False, - # help="Interactive mode" - # )(function) + + function = click.option( + "interactive", + "--interactive/--no-interactive", + "-I", + is_flag=True, + show_default=APP_TTY, + default=APP_TTY, + help="Interactive mode", + )(function) + + function = click.option( + "colors", + "--colors/--no-colors", + "-C", + is_flag=True, + show_default=APP_COLORS, + default=APP_COLORS, + help="Enable colors", + )(function) + # function = click.option( # "recursive", "--recursive", "-r" # is_flag=True, show_default=False, default=False, # help="Recursive mode" # )(function) + # function = click.option( + # 'dry_run', '-n', '--dry-run', + # is_flag=True, show_default=False, default=False, + # help="Dry run, do not take any actions." + # )(function) function = click.option( "quiet", @@ -171,12 +194,6 @@ def global_options(function): def format_options(function): - # function = click.option( - # 'dry_run', '-n', '--dry-run', - # is_flag=True, show_default=False, default=False, - # help="Dry run, do not take any actions." - # )(function) - # Duplicates function = click.option( "-v", @@ -243,6 +260,8 @@ def cli_app( config: str = DEFAULT_IDENT, ident: str = DEFAULT_IDENT, quiet=False, + interactive=False, + colors=False, log_trace=False, fmt_fields=None, fmt_sort=None, @@ -255,6 +274,15 @@ def cli_app( is not all that interesting. """ + # Show trace on errors, more like the --trace flag + if log_trace: + global APP_EXCEPTION + APP_EXCEPTION = None + + # Disable COLORS + global APP_COLORS + APP_COLORS = colors + # Calculate log level log_level = DEFAULT_LOG_LEVEL - verbose * 10 log_level = log_level if log_level > 0 else 10 @@ -272,6 +300,8 @@ def cli_app( log_level=log_level, fmt_name=fmt_name or DEFAULT_FORMAT, ident=ident, + interactive=interactive, + quiet=quiet, ) render_config = { "fmt_name": cli_config.fmt_name, @@ -288,7 +318,7 @@ def cli_app( # Instanciate app logger.info(f"Current ident: {ident}") ctx.obj = SimpleNamespace( - app=App(config_path=config, ident=ident), + app=App(config_path=config, ident=ident, cli_context=cli_config), cli=cli_config, render_item=render_item, render_list=render_list, @@ -892,12 +922,13 @@ def cli_app_run(ctx, cmd_params): if show_list: ret = app.catalog.services.list_cmds() - data = [[x.name, x.desc] for x in ret] + pprint(ret) + data = [[x.cmd.name, x.cmd.desc, x.svc.name] for x in ret] # Render data ctx.obj.render_list( data, - ["name", "desc"], + ["name", "desc", "service"], conf={ "table_title": f"Services commands", # "fmt_name": fmt_name, @@ -969,11 +1000,18 @@ def run(): clean_terminate(err) # Developper catchall - raise Exception(err) from err - # logger.error(traceback.format_exc()) - # logger.critical("Uncatched error %s; this may be a bug!", err.__class__) - # logger.critical("Exit 1 with bugs") - # sys.exit(1) + if APP_COLORS: + console.print_exception(show_locals=True) + else: + # raise Exception(err) from err + + logger.error(traceback.format_exc()) + if APP_EXCEPTION: + logger.critical("Uncatched error %s; this may be a bug!", err.__class__) + logger.critical("Exit 1 with bugs") + else: + logger.critical("Exited with %s error: %s", err.__class__.__name__, err) + sys.exit(1) if __name__ == "__main__": diff --git a/iam/exceptions.py b/iam/exceptions.py index 23c47bb..c849d0e 100644 --- a/iam/exceptions.py +++ b/iam/exceptions.py @@ -2,6 +2,10 @@ class IamException(Exception): "Iam Exception" +# Second level classes +# ===================== + + class UnresolvedResourceDependencies(IamException): "Raised when resources dependency is unmet" @@ -20,3 +24,23 @@ class MissingConfigFiles(IamException): class UnknownServiceCommand(IamException): "Raised when a command is not matched against services" + + +# Configuration errors +# ===================== + + +class ConfigurationError(IamException): + "Raised when a command is not matched against services" + + +class ConfigDuplicateCmds(ConfigurationError): + "Raised when two service commands overlap. May be caused by plugins" + + +class ConfigForbiddenPrefixCmds(ConfigurationError): + "Raised when a service is not correctly prefixed. May be caused by plugins" + + +class ConfigUnknownPlugin(ConfigurationError): + "Raised when a plugin can't be found" diff --git a/iam/lib/utils.py b/iam/lib/utils.py index 706a6ba..9815662 100644 --- a/iam/lib/utils.py +++ b/iam/lib/utils.py @@ -1,4 +1,5 @@ import importlib +import importlib.util import json import logging import os @@ -72,6 +73,21 @@ def empty(item): return False +def duplicates(_list): + """Check if given list contains duplicates""" + known = set() + dup = set() + for item in _list: + if item in known: + dup.add(item) + else: + known.add(item) + + if len(dup) > 0: + return list(dup) + return [] + + def prune(items): "Prune empty lists, dicts and None values from list or dict" @@ -197,6 +213,16 @@ def to_hcl(data): # return tomllib.dumps(data) +def filter_existing_files(root_path, candidates): + """Return only existing files""" + result = [ + os.path.join(root_path, cand) + for cand in candidates + if os.path.isfile(os.path.join(root_path, cand)) + ] + return list(set(result)) + + def to_toml(data): "Render to toml" pprint(data) @@ -274,6 +300,18 @@ def get_pkg_dir(name): return os.path.dirname(mod.__file__) +def import_module_file(package, path): + "Simple helper to load dynamically python modules" + + spec = importlib.util.spec_from_file_location(package, path) + mod = importlib.util.module_from_spec(spec) + + sys.modules[package] = mod + spec.loader.exec_module(mod) + + return mod + + def import_module_pkg(package): "Simple helper to load dynamically python modules" return importlib.import_module(package) diff --git a/iam/plugins/base.py b/iam/plugins/base.py index 08f51fe..1b6a767 100644 --- a/iam/plugins/base.py +++ b/iam/plugins/base.py @@ -1,7 +1,21 @@ plugin_base = { "resources_def": { - # Secrets + # Main + # ------------- "secret": {"desc": "Secret", "input": {"secret": None}}, + "auth": {"desc": "Authentification"}, + "account": { + "desc": "Account", + "input": {"password": None, "user": None}, + }, + "service": {"desc": "Session service"}, + "scope": {"desc": "Scoped identity"}, + # Bases + "service.id": {"desc": "Default ident service"}, + "service.scope": {"desc": "Default scope service"}, + # Other assets: + # ------------- + # Secrets "secret.env": { "desc": "Environment secret" "vars", "input": {"secret_env": None}, @@ -11,7 +25,6 @@ plugin_base = { "input": {"secret_file": None}, }, # Auths - "auth": {"desc": "Authentification"}, "auth.password": {"desc": "Password", "input": {"password": None}}, "auth.token": { "desc": "Token", @@ -22,20 +35,15 @@ plugin_base = { "input": {"token": None}, }, # Accounts - "account": { - "desc": "Account", - "input": {"password": None, "user": None}, - }, "account.email": {"desc": "Email account", "input": {"email": None}}, - # Services - "service": {"desc": "Session service"}, - # ID - "service.id": {"desc": "Default ident service"}, }, "resources": { "service.id": { "enabled": True, }, + "service.scope": { + "enabled": True, + }, }, "services": { "id": { @@ -59,8 +67,29 @@ plugin_base = { }, }, }, + "scope": { + "desc": "Local scope identity/sub identities", + "commands": { + "shell_enable": { + "desc": "Enable shell scope", + "shell": "export SHELL_SCOPE={{scope}}", + }, + "shell_disable": { + "desc": "Disable shell scope", + "shell": "unset SHELL_SCOPE", + }, + "scope new": { + "desc": "Create shell scope", + "shell": "add_scope {{ param }}", + }, + "scope delete": { + "desc": "Delete shell scope", + "shell": "rm_scope {{ param }}", + }, + }, + }, }, } -all = {"base": plugin_base} +providers = {"base": plugin_base} diff --git a/iam/plugins/devops.py b/iam/plugins/devops.py index 9c16231..9e19b05 100644 --- a/iam/plugins/devops.py +++ b/iam/plugins/devops.py @@ -2,9 +2,9 @@ import os from iam.lib.utils import get_pkg_dir, open_yaml, to_yaml -all = None +providers = None yml_dir = get_pkg_dir(__name__) conf_files = open_yaml(os.path.join(yml_dir, "devops.yml")) for conf in conf_files: - all = conf.get("providers", {}) + providers = conf.get("providers", {}) break diff --git a/iam/plugins/local.py b/iam/plugins/local.py deleted file mode 100644 index 6436339..0000000 --- a/iam/plugins/local.py +++ /dev/null @@ -1,10 +0,0 @@ -import os -from pprint import pprint - -from iam.lib.utils import get_pkg_dir, open_yaml, to_yaml - -yml_dir = get_pkg_dir(__name__) -plugin_conf = open_yaml(os.path.join(yml_dir, "local.yml"))[0] - - -all = plugin_conf.get("providers", {}) diff --git a/iam/plugins/local.yml b/iam/plugins/local.yml deleted file mode 100644 index af70a80..0000000 --- a/iam/plugins/local.yml +++ /dev/null @@ -1,404 +0,0 @@ - -providers: - - # Provider: SSH - # ================== - ssh: - - services: - local.ssh_key: - desc: Local ssh key - inputs: - ssh_key_secret: "" - ssh_key_alg: "ed25519" - - commands: - - ssh new: - desc: Create new SSH key - - - # shell: | - # echo "This is my shelll !!! $SHELL" - # env | grep jez - - # cmd: | - # env - # # echo Create ssh key: {{ssh_key_alg}} for ident '{{ident}}' with pass: {{ssh_key_secret}} - - shell: | - # env | sort - # echo - - - SSH_KEY_ALG={{ssh_key_alg}} - - SSH_KEY_VERSION="$(date +'%Y%m%d')" - SSH_KEY_HOST="$(hostname -f)" - - SSH_KEY_FILE=$HOME/.ssh/{{ident}}/{{user}}_${SSH_KEY_ALG}_${SSH_KEY_VERSION} - SSH_KEY_COMMENT={{user}}@${SSH_KEY_HOST}:${SSH_KEY_ALG}_${SSH_KEY_VERSION} - - echo mkdir -p $HOME/.ssh/{{ident}}/ - echo ssh-keygen -f "${SSH_KEY_FILE}" \ - -t ed25519 -a 100 \ - {% if ssh_key_secret %}-N "{{ssh_key_secret}}"{%endif%} \ - -C "$SSH_KEY_COMMENT" - - - - ssh delete: - desc: Delete existing SSH key - cmd: | - find $HOME/.ssh/{{ident}}/ -name "{{user}}_*" - - - - - resources_def: - - - auth.ssh_certificate: - desc: SSH Certificates - input: - ssh_cert_file: null - needs: - - auth.ssh_key - - auth.ssh_key: - desc: SSH Keypair - input: - ssh_key_file: null - ssh_key_secret: null - needs: - - kind: auth.password - remap: - ssh_key_secret: passord - - account.ssh: - desc: Unix account - input: - host: null - - - # service.local.ssh_key: - # desc: A local ssh key - - - - # resources: - - # service.local.ssh_agent: - # enabled: true - - # service.local.ssh_agent_keys: - # enabled: true - # loop: - # - auth.ssh_key:{ident}/ed25519 - # - auth.ssh_key:{ident}/rsa4096 - # - auth.ssh_key:{ident}/rsa2048 - # - auth.ssh_key:{ident}/rsa1024 - # - auth.ssh_key:{ident} - # loop_limit: 3 - - - - - # Provider: GPG Agent - # ================== - gpg_agent: - - - resources_def: - - auth.gpg_key: - desc: GPG keypair - input: - gpg_key_file: null - gpg_key_secret: null - needs: - - kind: auth.password - remap: - gpg_key_secret: passord - - - # Provider: SSH Agent - # ================== - ssh_agent: - - services: - - local.ssh_agent: - desc: Local ssh-agent - input: - ssh_agent_socket_dir: /run/user/ssh-agent - ssh_agent_tmout: 7d - - commands: - - shell_start: - desc: Start ssh-agent - shell: | - socket=$HOME/.local/state/ssh-agent/{{user}} - start=true - - running=2 - if [[ ! -e "$socket.env" ]]; then - running=2 - elif [[ -e "$socket" ]]; then - running=$(SSH_AUTH_SOCK=$socket ssh-add -l &>/dev/null; echo $rc) - fi - - if [[ "$running" -eq 2 ]]; then - # Start agent - >&2 echo "Start ssh-agent for {{ident}}" - mkdir -p "${socket%/*}" - ssh-agent -a $socket -t {{ssh_agent_tmout}} > $socket.env - fi - - unset socket start running - - # if [[ -d "/run/user/$(id -u)" ]]; then - # socket=/run/user/$(id -u)/ssh-agent/{{user}} - # else - - # fi - - - shell_enable: - desc: Enable ssh-agent - - shell: | - socket=$HOME/.local/state/ssh-agent/{{user}} - - if [[ -e "$socket.env" ]]; then - # >&2 echo "Enable ssh-agent for {{ident}}" - source "$socket.env" >/dev/null - fi - - unset socket - - - - shell_disable: - desc: Disable ssh-agent - shell: | - unset SSH_AUTH_SOCK SSH_AGENT_PID - - - shell_stop: - desc: Kill ssh-agent - shell: | - socket=$HOME/.local/state/ssh-agent/{{user}} - - if [[ -e "$socket.env" ]]; then - # >&2 echo "Enable ssh-agent for {{ident}}" - source "$socket.env" >/dev/null - # fi - - # if [[ -n "$SSH_AGENT_PID" ]]; then - >&2 echo "Kill ssh-agent for {{ident}}" - eval "(ssh-agent -k)" >/dev/null - [[ -e "$socket.env" ]] && rm "$socket.env" || true - fi - unset socket - - # env_file="$HOME/.local/state/ssh-agent/{{user}}.env" - - # if [[ -f "$env_file" ]]; then - # source "$env_file" - # fi - # if [[ -f "$env_file" ]]; then - # rm "$env_file" - # fi - - - local.ssh_agent_keys: - desc: Local ssh-agent keys - - commands: - ssh add: - desc: Unload keys into ssh-agent - shell: ssh-agent -d {ssh_key_file} - - ssh rm: - desc: Load keys into ssh-agent - shell: | - ssh-add {% for item in loop %} {{item.ssh_key_file}} {% endfor %} - - - required_services: - - local.ssh_agent - - - resources_def: - - service.local.ssh_agent: - desc: Configure ssh-agent daemon - - service.local.ssh_agent_keys: - desc: Configure ssh-agent keys autoloader - - - resources: - - service.local.ssh_agent: - enabled: true - - service.local.ssh_agent_keys: - enabled: true - loop: - - auth.ssh_key:{ident}/ed25519 - - auth.ssh_key:{ident}/rsa4096 - - auth.ssh_key:{ident}/rsa2048 - - auth.ssh_key:{ident}/rsa1024 - - auth.ssh_key:{ident} - loop_limit: 3 - - - - - - # Provider: Git Config - # ================== - git: - - services: - - local.git: - desc: Git identity - # input: - # ssh_agent_socket_dir: /run/user/ssh-agent - # ssh_agent_tmout: 7d - - commands: - - shell_enable: - desc: Enable git identity - shell: | - export GIT_AUTHOR_NAME='{{ident}}' - export GIT_AUTHOR_EMAIL='{{email}}' - export GIT_COMMITTER_NAME='{{ident}}' - export GIT_COMMITTER_EMAIL='{{email}}' - - - shell_disable: - desc: Disable git identity - shell: | - unset GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_COMMITTER_NAME GIT_COMMITTER_EMAIL - - - local.git_home: - desc: Home as git repo - input: - git_dir: "$HOME" - git_work_tree: $HOME/.local/share/home_git - - commands: - - shell_enable: - desc: Enable git home management - shell: | - export GIT_DIR="{{git_dir}}" - export GIT_WORK_TREE="{{git_work_tree}}/{{ ident }}" - - shell_disable: - desc: Disable git home management - shell: | - unset GIT_DIR GIT_WORK_TREE - - required_services: - - local.git - - resources_def: - - service.local.git: - desc: Configure git - - service.local.git_home: - desc: Configure home as git repo - - resources: - - service.local.git: - enabled: true - uses: - - account:{user} - - # Disabled by default - service.local.git_home: - - - - # Provider: PS1 Config - # ================== - ps1: - - services: - - local.ps1: - desc: PS1 prompt - input: - enabled: True - - commands: - - shell_enable: - desc: Enable PS1 - shell: | - export OLD_PS1=$PS1 - export PS1="\033[0;34m\]({{ident}})\033[00m\] ${PS1}" - - shell_disable: - desc: Disable PS1 - shell: | - export PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ ' - # export PS1="$OLD_PS1" - - - - - resources_def: - service.local.ps1: - desc: PS1 prompt - - - resources: - - service.local.ps1: - desc: Custom Ident PS1 - - - -# EXISTING - -# WARN__: Your workspace is already activated -# NOTICE: Enabling id ... -# export SHELL_ID='mrjk' -# export GIT_AUTHOR_NAME='mrjk' -# export GIT_AUTHOR_EMAIL='mrjk.78@gmail.com' -# export GIT_COMMITTER_NAME='mrjk' -# export GIT_COMMITTER_EMAIL='mrjk.78@gmail.com' - -# NOTICE: Enabling gpg ... -# export GNUPGHOME=/home/jez/.config/gpg/mrjk -# export GPG_AGENT_INFO=/run/user/1000/pgp-agent/mrjk/socket -# export GPG_DEFAULT_ID=mrjk -# export GPG_TTY=/dev/pts/48 -# export GNUPGHOME=/home/jez/.config/gpg/mrjk - -# NOTICE: Enabling ssh ... -# export SSH_AUTH_SOCK=/run/user/1000/ssh-agent/mrjk/socket - -# NOTICE: Enabling gh ... -# export GH_TOKEN="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" -# export GH_REPO="mrjk" - -# NOTICE: Enabling gitea ... -# export GITEA_SERVER_URL="ad808bc88fa37bce5e3bb963f1420aa575194d30" -# export GITEA_LOGIN="mrjk@git.jeznet.org" - -# NOTICE: Enabling ps1 ... -# export PS1="\[\](mrjk)\[\] ${IDM_SHELL_PS1}" - -# NOTICE: Identity 'mrjk' is loaded