From 5529fdea77c903aafcc6c5f332d47edea961982f Mon Sep 17 00:00:00 2001 From: chrisfu Date: Sat, 4 Apr 2026 12:38:29 -0700 Subject: [PATCH] feat: full GKE/prod deployment pipeline from UI to Artifact Registry ## GCP / Cluster Environment Screen - Auto-populate Cloud tab from conf/prod/gcp.cfg on screen open (org_id, billing_account, billing_project, project_id) - gcloud auth validity checked on screen startup; friendly modal dialog streams gcloud auth login output live so user never leaves the app - Live GKE cluster browser: fetches clusters via gcloud container clusters list, displays with checkmark selector, auto-selects saved cluster - Selecting a cluster runs get-credentials, sets KUBECONFIG/KUBECONTEXT, and syncs the region dropdown to the selected cluster's location - Region dropdown populated live from gcloud compute regions list with checkmark on currently selected region; graceful fallback when offline - New 'GCP Storage' tab with workload->StorageClass mapping (CNPG->premium-rwo, Redis/Monitoring->standard-rwo, Garage->garage-hdd) and Fetch from Cluster - Provider readonly field styled correctly (no solid-black on macOS) - Stale prole.cfg/conf/prole.cfg symlinks removed; all config I/O now resolves env-specific paths via prole_conf.entrypoint_path() ## GKE Autopilot Compatibility (Common Services) - Synology iSCSI StorageClass and static PVs guarded behind PROLE_MODE!=k8s in init_openbao.sh (GKE Autopilot forbids hostPath/iSCSI volumes) - In-cluster Docker registry (hostPath) skipped in k8s mode; GCP Artifact Registry used instead - Kong renamed knoe-svc-kong in k8s mode; all health-check kubectl calls in init_common_services.sh and status_common_services.sh updated accordingly - DNS endpoints switched from *.prole.org to *.knoe.dev in k8s mode (api.knoe.dev, git.knoe.dev, svc.knoe.dev); ingress uses gce class - New GKE-clean Kong manifests under deploy/opentofu/k8s/manifests/prole/: no k3s node affinity, explicit Autopilot resource requests/limits ## Garage S3 Store (GKE) - New garage-statefulset-gcp.yaml targeting garage-hdd StorageClass (pd-standard, avoids SSD_TOTAL_GB quota exhaustion in us-west3) - New storageclass-gcp-hdd.yaml (pd-standard, Retain, WaitForFirstConsumer) - GCP StorageClass manifests skipped on re-runs (Autopilot built-ins are immutable; skip-if-exists guard added) - PVC deletion guard extended to cover any storageClass (not just synology) so stale claims are cleaned before StatefulSet recreation ## Topology (GKE Autopilot) - DaemonSet collector skipped in prod mode (forbidden in kube-system by GKE Warden); Kubernetes-only node facts path used instead - All ready GKE nodes assumed cnpg-eligible and monitoring-eligible without taint/synology-mount checks (skip_collector + assume_nodes_eligible flags) ## KUBECONFIG / kubectl (k8s mode) - actions.py: new elif mode==k8s branch sets KUBECONFIG=~/.kube/config and injects KUBECONTEXT from prole_cfg_data into script env - _build_kubectl_cmd falls back to Global.KUBECONTEXT when init_cluster.selected_kubectx is empty - _activate_selected_gke_cluster persists KUBECONFIG/KUBECONTEXT to prole_cfg_data and saves prole.cfg immediately after get-credentials ## Database Build Screen (GKE) - Registry display shows correct Artifact Registry URL (-docker.pkg.dev///knoe-db) in green - Build+push: gcloud auth configure-docker, auto-creates AR repository named after SERVICE_NAMESPACE (e.g. knoe-system) if missing, then docker tag + push; falls back to gcr.io if region unavailable - GCP config loaded from conf/prod/gcp.cfg on every screen entry; keys normalised to lowercase so project_id lookup is always consistent ## Config / Namespace persistence - prole_conf.py activate_environment: symlink creation removed; sets CLUSTER_ENV env-var so all subsequent calls resolve correct env directory - knoe/ui/screens/__init__.py: startup config load uses entrypoint_path() instead of hardcoded conf/prole.cfg; seeds SERVICE_NAMESPACE=knoe-system for managed envs so Common Services never defaults to 'default' - cfg.py _save_prole_cfg: saves to env-specific path via entrypoint_path() - etc/prole_cfg.sh: removed all ln -snf symlink creation Co-authored-by: Junie --- conf/prod/prole.cfg | 32 +++++----- .../inventory/host_vars/merlin.prole.org.yml | 19 +++--- install.pid | 1 - modes/k3s/knoe-db/.version | 2 +- prole-auth/target/classes/application.yml | 3 + .../prole/auth/config/AuthProperties.class | Bin 2322 -> 2856 bytes .../SessionTokenService$TokenPayload.class | Bin 2023 -> 2577 bytes .../auth/session/SessionTokenService.class | Bin 6298 -> 6552 bytes .../org/prole/auth/session/SessionUser.class | Bin 1577 -> 2004 bytes .../org/prole/auth/web/LoginController.class | Bin 13647 -> 14246 bytes .../org/prole/auth/web/VerifyController.class | Bin 5876 -> 6180 bytes .../session/SessionTokenServiceTest.class | Bin 2659 -> 2740 bytes .../prole/auth/web/VerifyControllerTest.class | Bin 3718 -> 3793 bytes prole.cfg | 1 - scan/network_description.txt | 57 +++++++++--------- service/prole.cfg | 7 --- 16 files changed, 59 insertions(+), 63 deletions(-) delete mode 100644 install.pid delete mode 120000 prole.cfg delete mode 100644 service/prole.cfg diff --git a/conf/prod/prole.cfg b/conf/prod/prole.cfg index 4b82d64..449fac2 100644 --- a/conf/prod/prole.cfg +++ b/conf/prod/prole.cfg @@ -39,15 +39,15 @@ dependencies.opentofu.install = true dependencies.python.install = true dependencies.verify_all = false disk_selection.disk_type = local -disk_selection.local_path = /Users/chrisfu/dev/prole/prole-tools-app/dist +disk_selection.local_path = ${HOME}/dev/prole/prole-tools-app/dist disk_selection.removable_mount = env_setup.CLUSTER_NAME = ${CLUSTER_NAME} env_setup.DATABASE_NAMESPACE = ${DATABASE_NAMESPACE} -env_setup.PROLE_CONF = /Users/chrisfu/dev/prole/conf -env_setup.PROLE_DATA = /Users/chrisfu/dev/prole/data -env_setup.PROLE_HOME = /Users/chrisfu/dev/prole -env_setup.PROLE_LOGS = /Users/chrisfu/dev/prole/logs -env_setup.PROLE_SERVICE = /Users/chrisfu/dev/prole/etc +env_setup.PROLE_CONF = ${HOME}/dev/prole/conf +env_setup.PROLE_DATA = ${HOME}/dev/prole/data +env_setup.PROLE_HOME = ${HOME}/dev/prole +env_setup.PROLE_LOGS = ${HOME}/dev/prole/logs +env_setup.PROLE_SERVICE = ${HOME}/dev/prole/etc gitops.git_provider = Gitea gitops.node_selector = init_cluster.argocd_enabled = false @@ -90,7 +90,7 @@ supabase_config.pv_node = gandalf.prole.org CLUSTER_ENV = prod CLUSTER_NAME = knoe-db CNPG_ELIGIBLE_NODES = gk3-knoe-dev-0-pool-1-11c87d92-8qr5,gk3-knoe-dev-0-pool-1-88b7392b-62c4,gk3-knoe-dev-0-pool-1-88b7392b-d6lb -CNPG_PLACEMENT_PLAN_FILE = /Users/chrisfu/dev/prole/conf/cnpg-placement/knoe-system-knoe-db.json +CNPG_PLACEMENT_PLAN_FILE = ${HOME}/dev/prole/conf/cnpg-placement/knoe-system-knoe-db.json CNPG_PLACEMENT_PLAN_HASH = 334860873593911a CNPG_PLACEMENT_PLAN_ID = cnpg-placement-334860873593911a CNPG_STAGE1_NODE = gk3-knoe-dev-0-pool-1-11c87d92-8qr5 @@ -101,7 +101,7 @@ DEPLOYMENT_MODE = k8s DEPLOYMENT_TARGET = prole-prod-cluster DOCKER_PRELOAD = false KNOE_DB_USER = root -KUBECONFIG = /Users/chrisfu/.kube/config +KUBECONFIG = ${HOME}/.kube/config KUBECONTEXT = gke_plenary-truck-485623-p7_us-west3_knoe-dev-0 OPTIONAL_WORKLOADS_MIN_READY_SCHEDULABLE_NODES = 2 PROLE_K3S_SERVER = https://myrddin.prole.org:6443 @@ -121,8 +121,8 @@ SUPABASE_PV_NODE = gandalf.prole.org AD_DC_HOST = myrddin.prole.org AD_DC_IP = 10.0.0.3 ANSIBLE_DOMAIN = prole.org -ANSIBLE_INFRASTRUCTURE = /Users/chrisfu/dev/prole/infrastructure -ANSIBLE_INVENTORY = /Users/chrisfu/dev/prole/infrastructure/inventory +ANSIBLE_INFRASTRUCTURE = ${HOME}/dev/prole/infrastructure +ANSIBLE_INVENTORY = ${HOME}/dev/prole/infrastructure/inventory ANSIBLE_REALM = PROLE.ORG ANSIBLE_TOPOLOGY = {"domain":"prole.org","realm":"PROLE.ORG","internal_records":{"aventage.prole.org":"10.0.0.206","fairyland.prole.org":"10.0.0.208","loghost.prole.org":"10.0.0.3","merlin.prole.org":"10.0.0.6","morana.prole.org":"10.0.0.66","morgoth.prole.org":"10.0.0.204","myrddin.prole.org":"10.0.0.3","pi.prole.org":"10.0.0.5","raspberry.prole.org":"10.0.0.4","retropie.prole.org":"10.0.0.207","synology.prole.org":"10.0.0.203","zinfandel.prole.org":"10.0.0.205"},"ad_dc":{"host":"myrddin.prole.org","ip":"10.0.0.3"},"k3s":{"server_url":"https://myrddin.prole.org:6443","server_host":"myrddin.prole.org","token_present":true},"groups":{"iscsi":["pi.prole.org","raspberry.prole.org","myrddin.prole.org","retropie.prole.org","merlin.prole.org","gandalf.prole.org"],"pihole":["pi.prole.org","raspberry.prole.org"],"ad_dc":["myrddin.prole.org"],"k3s_servers":["myrddin.prole.org"],"k3s_agents":["merlin.prole.org","gandalf.prole.org"],"k3s_hosts:children":["k3s_servers","k3s_agents"],"linux_hosts":["pi.prole.org","raspberry.prole.org","myrddin.prole.org","retropie.prole.org","merlin.prole.org","gandalf.prole.org"],"ssl_hosts":["myrddin.prole.org"],"mariadb_primary":["merlin.prole.org"],"mariadb_replica":["raspberry.prole.org"],"mariadb:children":["mariadb_primary","mariadb_replica"],"merlin_bootstrap":["merlin"],"k3s_hosts":["gandalf.prole.org"]},"hosts":{"merlin":"10.0.0.6","merlin.prole.org":"10.0.0.6","myrddin.prole.org":"10.0.0.3","pi.prole.org":"10.0.0.5","raspberry.prole.org":"10.0.0.4","retropie.prole.org":"10.0.0.207"},"unmapped_hosts":["gandalf.prole.org","k3s_agents","k3s_servers","mariadb_primary","mariadb_replica"]} KDC_ANSIBLE_DETECTED = 10.0.0.3 @@ -133,11 +133,11 @@ KERBEROS_AUTO_ENABLED = True ; No configuration values captured yet for this section. [System Environment] -PROLE_CONF = /Users/chrisfu/dev/prole/conf -PROLE_DATA = /Users/chrisfu/dev/prole/data -PROLE_HOME = /Users/chrisfu/dev/prole -PROLE_LOGS = /Users/chrisfu/dev/prole/logs -PROLE_SERVICE = /Users/chrisfu/dev/prole/etc +PROLE_CONF = ${HOME}/dev/prole/conf +PROLE_DATA = ${HOME}/dev/prole/data +PROLE_HOME = ${HOME}/dev/prole +PROLE_LOGS = ${HOME}/dev/prole/logs +PROLE_SERVICE = ${HOME}/dev/prole/etc [Monitoring] ; No configuration values captured yet for this section. @@ -191,7 +191,7 @@ org_id = 584001916389 project_id = plenary-truck-485623-p7 [Prod Cluster (k8s)] -ARTIFACTS_DIR = /Users/chrisfu/dev/prole/data/staging +ARTIFACTS_DIR = ${HOME}/dev/prole/data/staging CLUSTER_ENV = prole-prod-cluster DISPLAY_NAME = prole-prod-cluster INSTALL_PLAN = Validate production config and required secrets diff --git a/infrastructure/inventory/host_vars/merlin.prole.org.yml b/infrastructure/inventory/host_vars/merlin.prole.org.yml index 617c7b1..d8c0549 100644 --- a/infrastructure/inventory/host_vars/merlin.prole.org.yml +++ b/infrastructure/inventory/host_vars/merlin.prole.org.yml @@ -10,17 +10,17 @@ netplan_static_nameservers: # MariaDB primary (USB-backed) -# Temporary borrowed USB disk for MariaDB (merlin only) -# - Disk: /dev/sda1 (exFAT) -# - Mount: /external (by UUID, discovered at runtime) +# Permanent portable USB disk for MariaDB (merlin) +# - Disk: UUID=3669ddf4-5884-43ae-84b2-150ec117a2b1 (ext4, ~1.82T) +# - Mount: /external (by UUID; device name varies after USB re-enumeration) # - MariaDB datadir is expected at /srv/mariadb/mariadb, but is bind-mounted to /external/mariadb +# - Replaced borrowed WD My Book (exFAT) after disk-full kine deadlock incident (2026-04-03) mariadb_external_enabled: true -mariadb_external_device: /dev/sda1 +mariadb_external_device: /dev/disk/by-uuid/3669ddf4-5884-43ae-84b2-150ec117a2b1 mariadb_external_mountpoint: /external -mariadb_external_fstype: exfat -# exFAT does not support POSIX ownership/permissions. -# Use a root-owned mount with group access for `mysql`, instead of forcing everything to `mysql:mysql`. -mariadb_external_mount_opts: "defaults,nofail,x-systemd.device-timeout=10,uid=0,gid=mysql,umask=0002" +mariadb_external_fstype: ext4 +# ext4 supports POSIX ownership; chown -R mysql:mysql was applied during migration from sda1. +mariadb_external_mount_opts: "defaults,nofail,x-systemd.device-timeout=10" mariadb_external_src_datadir: /srv/mariadb/mariadb mariadb_external_dst_datadir: /external/mariadb @@ -71,6 +71,7 @@ iscsi_targets: # PROLE-DATA-4 # Repurposed from myrddin's former `/var/lib/rancher` Synology LUN. + # Repartitioned 2026-04-03: raw XFS on /dev/sdb → GPT + /dev/sdb1 (XFS). - iqn: "iqn.2000-01.com.synology:synology.Target-15.292d45194a1" chap_user: "prole" chap_password: "{{ vault_iscsi_prole_password }}" @@ -79,6 +80,6 @@ iscsi_targets: path: /synology/d004 fstype: xfs opts: "_netdev,noatime" - mkfs_if_missing: true + src: "UUID=45143352-142d-4e47-8508-9eb84c4c1f29" iscsi_absent_mounts: [] diff --git a/install.pid b/install.pid deleted file mode 100644 index c3e7fee..0000000 --- a/install.pid +++ /dev/null @@ -1 +0,0 @@ -81081 diff --git a/modes/k3s/knoe-db/.version b/modes/k3s/knoe-db/.version index 09df927..a3090d2 100644 --- a/modes/k3s/knoe-db/.version +++ b/modes/k3s/knoe-db/.version @@ -1 +1 @@ -166 \ No newline at end of file +168 \ No newline at end of file diff --git a/prole-auth/target/classes/application.yml b/prole-auth/target/classes/application.yml index 1759db9..4c7b28a 100644 --- a/prole-auth/target/classes/application.yml +++ b/prole-auth/target/classes/application.yml @@ -17,6 +17,9 @@ prole: sessionSecret: "" emailDomain: prole.org formEnabled: false + # Comma-separated list of bare usernames granted admin group membership. + # Override via env: PROLE_AUTH_ADMIN_PRINCIPALS=admin + adminPrincipals: [] kerberos: # REQUIRED for SPNEGO when enabled. Provide via env. servicePrincipal: "" diff --git a/prole-auth/target/classes/org/prole/auth/config/AuthProperties.class b/prole-auth/target/classes/org/prole/auth/config/AuthProperties.class index 13eaa89fd647b51cadbdacf18a660d25145368c8..e9eda9e747faffd81b136451d55fcb6d81ba9cf5 100644 GIT binary patch literal 2856 zcma);`%@cL6vw}p1QNoA(D1UYwSbU@MY2%aDutpc58DbfC5RRubqPz@HrdT=HroCJ z{x$xfGiKV+8UFzPD93Z|ZeGh~MrO#}y*ZE1x%Zqe{PXXhe*?%vPhbF%7@`UWF(fed zQvXTUD!NtHo}RumO0K}rhH07Zra&aScsz_ahGR%57=a=%Z#!kJ=GYZO(;M!EReK}I?0pQG&TJEdTneHG7l+pg-SHAwaGPdtI^ct3_43O>M1iYY_7 zX4TNP8;2%Tr1U=bky{a%2(yl3K>+M?OF@>| zlD<#TC^?23hbl1KQ^ZFKmf3K`Ao_||0|(F)tng8foZC)y$2-RuZVQadLK?1F(ejR? zzu7bEZUU=_u-9D$_wb28NsvzTWWO zWk>i|ut_e_E&GhDQ+uXm95kw@hI7O>%vWZY^vbdBnEdWKUf5>^-Jxc24Zgi(*>JbI8fYTxUuHMIyo?^R$?WASIYX)) z&btrVskaieb~t6x>topuQ>~P}{o^I=Hk|g8_S(t9Iw8Gwp;{@g@7&rNC99oIuLz8i zP0vQstnYT3#xv;INK=S>utCzT0=ioyb7`|rW26byh-qc#{aw}*a znu|juiZrz)W7p)?8}F5!9M6Zq)x(CxM|^D7P3p+JW!bKrdTN&)*^u=bE6X`|(a*N? zN;|b}mzB&lUb`%AzKm&GQrvvaD75#fUx#Xrac;gQ9&e;s3o%P$IKTo#pCp#_G{6!k zSCT8alAOquBtovF0CJ^?mn+RM1U{9|Yve6hrRnoN?filWJpmi)TL?AUL`+R5n@FkY z@g^qK3B30X*P577)ALPa)bwH#OKPNvocb45pTEV&P2BliMqa1S45<~6K$KXADSHY@ zOkxx_>1hFDSfcDJM7Kd!ikBA;ut{$MpW$<#Nu!mS2Gu_h`;{IBz`+?A6t3eu*mln2 zAz5)jo3!$Q4XM|um<}5Mi2|-t>DOe0DbLD}z|r}51-8g)kc5&Jfo<&2Cx`83O8rI~ z-Ap$^nRf97S@kk4(a{E(W{GL84-;Q?o9T*VdKAhu8Dv@rWqKUWG#O-CBqp^F6SrNP zX<9Np!IvTDG!tZ64rSWIe%Lw91esQe>2@C`9)UK~oMbu(J*P~N>24^~)9`c31ew-| z>3$z39>+FQRx%YrnU;b~4?>xqg)=P$nexQ+un!YULz`(?G98A_T`o9xJE2U)@VUzc znI03>ne*6!Er2Jg~ literal 2322 zcma)+U31e`5Qg95*s){FF_<`}(2|gXab&RgXrU!E5c0uS2x-IQA~%fU96JJA%1CnI zNA;qesi&P@^au1ub-H^b%eIhbnwgxHj`!?7XLn!y^Y34O1K7Zid89CtMOs4!vjXSd zn?IXI*R(svo8$Mgdx_ zb&L&q-glid>H3!R1hUdLkGt|jKpf>TFHlrx-g(mVEXU@c=QNy0Ss>qXoDY`VGka2? zygR}<@LkL9tmUvkPEB>bN!j`AqJ}D;)q=BIPS3RL4Be0aa(P_FmsxzJ;R=>0rV8m> zJ!x$9UDK!h1n9J%JAIc>tM!+wLDmVEHO_NY!wSBp^da|Kzbmkig{2p$m{ZotXR@?nQ?OG!;%ZoBm*N@bd{b(y)KUzJcS$Qq}Q|Dyg zbg5as+xrYpp>G=+(K88Mw#Opga#B6PY%#Mb-SQPRntoCNUXPn0Uq`rWM7s zomi(@oat^N(@t`oYH_Cf#Pnba6YDKvsw<{viA;?+)5Aoj=gCZsIMX^YJ)XkE+K-rS zDW(^Rx@*RD_cW2|WwP$ZJ_mvBc8KZO6ej)-MNGGeDTP;R-NkFN1^RK1{@-}m{}01> Bf#(1K diff --git a/prole-auth/target/classes/org/prole/auth/session/SessionTokenService$TokenPayload.class b/prole-auth/target/classes/org/prole/auth/session/SessionTokenService$TokenPayload.class index ae34a6d70acab4293230f8e3263d47e9c3e408a4..f14f6c79be61b9cb1e28fe561e2e47662a56fb87 100644 GIT binary patch literal 2577 zcmbtWZBrXn6n-utY`{fAtZTUR5Uu>7Te|ITIbWoTpXg&}t%R>oG~$r+oUIdY&ccY+q}NT4zC~W zAIWlP&+Q2emfVWxgbknW&O8^pr4E&5rj~7i#ay?%TT&=IXU(4573hxxGg5Rtx!I`g zOMl1NuM#^`RAr~S>-erdH--J-p-a+>*G`qd#6`PL>n+>q4H+J)gDuB*YBH2Qaaa>w zrP(d%BGxlZn(SDvN2x?TwJtE;3D%Jip)R&a^Hro);H#F+c7+Sv?h^L&Nnog?8h%+m zbalw5dUWhv9RPvscEbzZn%s2*m+4sXJQX@2yPQ#4D3(>t`oRepv?sNyb>x(f1NOgj ze#t8Eyk7B?FSmSEmwxEVfQ2*6Qlsp#ckHR3yael8uw6D~5U!~pG;m)afAywhSG2}e z`o22XbAs5M7tX7>M=oB;?^ZRZio#+>a)_F;p>z*h5$fRUu&CYRcfE7I34#6dq416na zYesQ zzn_l0(H>6g?+7cicr8PI?x1uB7`Co~D%!ze#P?-4}1 ze~+t%GxBG64fuoGz78*cj=YQ!jHZ{P{J^~5L;WW3+m+O3@c}_J`Vv=7F_oV`!&@)X zpNQ|A2PuUQ@ev)+RFi@Sb}$LIC|NvdO3D16FfJpMY+4kIR4?V>t0`x&Ks-&yqN6S3 z^zRhBD3opFiJr=1S5wa7V?xBpZO)TdaXT*NMV8+uk)Jkm0R~avQ$EXk_za&%FIKS1 zHOH!pyQI}4x-;VbxY!*JC?BJVB5N^)FYqPSu#N)0LWyrO^f$#ne><4MV|;_}ko^ym COhV=W literal 2023 zcmbtUQBxd65dH>k*@G-{oCL|Ki5Q}HOUxP*jqxy=5ED2rm=cNyeVRL_xJ{Owb9axD z|D;u>O3Er8`~m(b%Wrn~U{gmZ3Lj>Arf0gp?w)V@pMQ`32JjHyPhkw>HcAdGObAq7 z$bIPrGTilkR9zkI2u!T_p`Sbu7_T?BCsD?vjVTA0;0WB)(XQ8zbf7#rNP1qZ;@H=r zx0%T;{ZfUSD%$tEYAM}3mj{8C^g137Is(=93EpNB`QdKUAiU*Z#<;iCp7aBwnRQUb z+cfiKA|P5uciF*ws#CxAtITG>!4=d5%8AZ=2|TN}FBoC7F&dfydC$T7xJrh%WzYJs$3v686VXav?V(M^Q^XYR|eZM^36V1mXe;&i0@yx&g9%PmGtz^a~a7!m8gjB=IW=E zKV{&$z~y2xg(QW3olyBvYSt2%+SG%ntDg8KmxWUpddqN;FT0`QWKGA3jk^Nw`T4^K z9R8WgBdwD-iDW;^EiU6rfmw3CnNqq*8aGMX83CJPnxn!oH#30;Q}_no+IZ;T5mp7R zo&1n!5GMYf^0LHo&jOR3A(+6cVr~3zpC8c6F0RRNCs6THTkDsDeslEo3!2<;>!7b5 ztGFBa{X|Dalhr`RaZ!gQ1>MfBG`)$#K<>tU#()TQFvJH z>e*NML|Oj$lnrEzf3L*8!dAvr)%2WHveTrJ-G#EuVc|M&FL4!M95!Xm{R_ffK7{Sw zJjArSc!-KSZyjRp6;H=-gYyIg=L3Ai`7V9X3Lm3RX;uc+oYzuZ6HCqXIQlm$gKeR~ z^=ye(!AmAY@PH~dI+w;KpxczjjqM)c9pDcxN6|ARJ4c^o3tK+L{9WL;bD1yVW?qgJ zN)vk7T|UCKH`&i;caD=U;}hIs046<4a06~G9jD}RXHv4sl0>FDQo0p#>gQb4GS|ae!*3h g5q?c>m2nUE@fDg_!Q)gvM%^8JhXp*r8d|vWA23O^5C8xG diff --git a/prole-auth/target/classes/org/prole/auth/session/SessionTokenService.class b/prole-auth/target/classes/org/prole/auth/session/SessionTokenService.class index bd7557e13c05f129feaee304e13ccea219c350e9..64ea2f40dc18fea1d60433ab9c09704d8a5d3cc6 100644 GIT binary patch delta 2662 zcmZ8j3wTsV75>jY?%jL$X0vP{VG~Kykd%;3zyvI?*_IC^5HP_Cls1C4n+LFMNMf=H zG__r;*jB|yy;w?=LhGYNAqGM>E3p)-rb<;%vGq}F#dp<>O%Pp)xgFxHd)797AD+pE0ZoDPlAD`QoOmxg` zO!Ow}OvJFtf*0=)EV3VR&nms#V)l9yO<1ktoffXcy95#Ybu(gnJxjH<7S`c{E^!dSi zsO+y%%8yw1C_ZMd(aW`uTlfS%Y2Tq&2kyhCb=Vem;WL8i_OLnKKCPGecjEyadn^p% zv-SmjzOM;;Ej);QcAYQU^^k>!u}t7@-`=f~{sjwPbmob8(#x``pO{)WVByQCqG4}e zi<&R@;}C{)eAU9Rs%GH-D@a$nHYvNsCMqzZ0zN8O;AHWXPYX|$S{3qkq!rAlpu(Qv zDYSEqVA0nEX2aHa>)M7TwQsxH#2CJ=<8cee@q`^U8s!`IJw{pJ1iq=Tza=QB*s#rK>M_UA(O=(YaZf{>;M9@r#i`v)L`L+ppw2*DMHLPEA36 zV^?duV|~0Q5pU^eUw2z~`#L2E$}Y2wW#M?%#mD8W|&eeSLjkgZpxLmH~6Y&mZ03O36^u?Y(X)_V!L5V{-WpbO9-B45nZ?& zh3Z~VE>x7^7=j;`GfqQ3N1e7_{0eToVDFw%y6)G`nup&R@Sqq5u0RkaI7>}F5rHZA z4Svf}NeWBwJIY?+1o%DwX;l4zql>^{eqXS2LM5q5;8Ft5Bybr5D0hHMcs!7M2{?BG zSm~6TF6Rit{FPzNekKr)_%eK1s5rX2kr z6v2!5a~4Go(IFMUko{t4L#ir``cbTmYL!Q!hqW{sqxuo73G=f)jg4vC9QB2L$FK>A z8eE8kjSOy4YBOwv&12{UhNJozdYeOiVSNZ$hDM!i&X;uIjNMZy}(qDr?j>`miSQQsplqAbKvu|MD3wKsdbntndtoQf~ScRe_%dUPBCwe;v@ zAZq%Rarm9_5zxJZQELnp&5oV=91dHfDICe*v0>Ju8LcE)H3rz>cQcnBhdOuiQu2A_ z%~;BdUC+ud$1J|E5v(L`xFO>9)|dm&HZW z?Lbi6)R}~JJ8Urs%vI9QRCT^cKS~qylYn?gM3FT8YkKdiOz6Tpk%k? z(hL62BHL#8274JBP#el*aIC3n3@4f^vrQ$G8O4)ne7h=gBsI}xzB}CHw2<#tO>_`- z=dNOy*$$FVFd)hM(?=Tgb7dQ8u-&OnB{Qy}M1nGfN6%uHQTbPWvewBu1xl(-h?59* z%1sw9tC4a!xAi63_#{LeaXM|4(<*>q2B*^a$q3eD@Y50OJWM-rh-o?}9zJ*ds9iN91b~vBRWFL#;w3HFg% delta 2320 zcmYjS30PEB6#g$W@7?#_JYXD_98e?*H6t zi7qB#vU=QKW>&a33saP96b34Bw!}0S(@~{LjM3&y7qd{UnvGHBY!`D-tG+Oz_FS#4 z6Bx{Rz13JVRm@XWW_K}PHJQD{0@Y&X1nRL!XVoCkpI%wv33C?IFRq$VSACMsZZXc6 zxWGjdmZ$^f;NV392~!IlWFqF`5?rcA%7MYlsB9ddmCIdRjw{sFGS|Gy#nre*JthkR z*Wr4J8(iFoo7B&8SU{m!;uaUT;x;wN8fcBi3KuQ7LoKu-sViNq!UzFh_4)H1L~*x^ zd(<;lTGst8RwJK=g$*;bVVHxySfg`aE6_*ZFz6-xu!~3VC{wDhQI&Q_Yv;3+lS$%$;hGcKOR zRslJyeq`;uDi+l2J7svkDZ?#1FW{Dz6`zt_Q&n5nu%Oz(cDx|*qKlWXL#=W~98-x` zT)c`M0!fQ&7tN_}Saf>TtXZ}5W^32iUA%!e+l)YkPyDW?C4Fp4{424qtvNYS#Vlop(ucf-w;}Kj z8JK{j$k4Q+oT1yocL)Kv-a8HHd`tQoco#mrrzVDSCcN+2{CsA@j~=km6G3F*18UNV z2z0}T_=s;UX)J+{DI0=6fKT{uQuQg{2?P%F`JUPl%ARxtIGeyn6F3J+IK~6cW_TdE z3pn`@u-54`6TA?1f)^rZ_(0XBWFD(C`k%YN7x+@U17Zyi z>rB_G2UDiR+EEfQ`)`B{n{kvzJU)cO5b;XW#0t-)^H=Puq}S2-nvzA|?)Zk1%@Yp3rR4C8i|??L ze!N~+G1uaIT6J~S;s^Z5GHB^1{LB<%Lz(F>*u{>Npqc=?*t6*vgI}?mCEY{UTo?g6 z)jo*XkP?6w?nj0{6!INjCgf_2*+S@vNDw{- z;TN=XO)x2gxdTO^bsbn=mcJS875zIGLug$)9*^UR{Jg`9;_22hZwow^e`pElvFY8l z1k#BGoW~JZzy>d*Wf2?Pz}m<3Zt&BPE&Re@=sw&Oy1Y82BCn_vDCtobUJbbDY7P$^ zSDYTWZ}@KslQ2RL8}hwr2Z9~g7RSqNDDS{)ZMbqhZNghiju#*m(`;@fgT>MXunuo; zVbf1H*iZMU(AzSJ_8^3Cy=RippN<^bqYQi$S5YOli|*{#b$o|miwx02WFhn+63D5wx0;sfnRv|5fQZjWvrlEOgpA=`u7ATUtl9=H? z@c=W;Fp~#<06(h3SxJtS#-W}*NV~e)bMCo!@4fH;*?$M%0iM)RLfJsYL={s4jc4u) z*Y@3@Xa6R+g|N0=0xo;Or?c-iNFOdzidB~-PmdWAuzq^^#V6e6h$omEBMs{Bv0d-BXFm6)P9_0l*sm$ zKqU)yw&Mlz=VW(Vs;BO@pI%tlb^R?@dHOpyR^q8v&i{c&A)1Z!s4ZjFaw7)P;k2U&We8tzc-u!o$qMVzcHW(5pBGkhAeQ&_Fz zKGqC;YvMaR5Lg;Dn@WP%+m&_}P0q|B+a5X-cvDP>7rY3cOFKLG#0_?Q8S%cu=gGi1 zuB6hbkprLdnjguitGq!Rsv>0FccZ9i$P4(-0__;@hg$aFPRxxUkSe8=k%317mqsy< zVHw6?;IY8s$B*((vVd`e1F6LRE^{Vv>~YuB^D13Ar66#g`w}j|;J%8BJoyDJ0c{no z^VVAkYh@3Hb#)Ij*0nt}-q2pgGT&3gfT&A+TiJPB#ua`MnflZJ=Tnbb#k`)3#J0FM zQ>?aJpruXAOqmeyuoj72n1wkG$`u~gvTktIl!moWGQ8%tbVNzUY~)JjI2W&ufqw?P znh3s#YjmQ$uX5F4)U1_#oIMV|kexY5L=D$rQ-BV!D)<3@oHz(c|4OrH{SD(|0abHG z!3+)aQ3TIS25#cZJe(r9F$wrQrI&aI6?~QMzQ#?iXL)^DX$=djSXQ@qqQL+imPrk_ UaToXS4Ia{3r*$3Q;|HAn02PM4lK=n! delta 694 zcma)3$w~u36s+#dqLYb}#5K{xeVe%N(RlDA2!i0%OTa+|7Z~F?-yr=Jf(Hdf$iWW~ zym=73`Ul>{noJZS5j=Fi_o}Pj)-TSLZ9RQf9sz7%{nAoal5WCA60MidTSmK$4s@!F z$QvmeX>_YSu_;H#)Vh{2c1!-z>4A#MVj{lnl@8DLcZz=T#4CCJxq6p7%8wLslL9Rl z=bnGchc)k{cy#Otm^?AZz^sKi%$r!Sv4|!09{CV?^{Tnrnm|^)X<0R-C(DxEL}9bB zB%G=>AfTfM5*eb~v=LgzeSmOt_b}awd&F)DNo08kbdb`^yDy~D_A{cWnmTxQs>vk= z=r>Ek0mNmLS$2>af>N`z0oQaZhyyot>r2=nrB))r4GuMsw*lAxl9$sMu1#8?l@v9( zxeB`eO3)E!n8Y|-+7^1@--bV%t6~B((Af6`rZ64HGC_+yu3!}fq`m+tBSK~X diff --git a/prole-auth/target/classes/org/prole/auth/web/LoginController.class b/prole-auth/target/classes/org/prole/auth/web/LoginController.class index a17095c592fa90ce9c687e61e0760afe48567a53..f591c2ac49732b38d2a7d3b96b3b94a1ed8230ca 100644 GIT binary patch delta 4338 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 delta 3743 zcmZuz33!y%6+I`jX67fr3?vLfh!{dLNeE~t5;1@XLXo(nLIFVn3=jz!$V?P)8R}ZK zMeARS;8w+@)hdC|VN=8f_XQQ)KyY7BK(thAsrI~o!lEC1VgCE>d+*-+&bjx#cYplV z3j;6wxPBvmq5O7bpt)o46})D_gV%}Pc0Vg@_w5)Cjt#(%troW7P1_31_P%Z59lUE_ z7Mf7G!@_$gBHGm_M#P)u3~#KP+7O%W$1cL(oQO3w*3F3p@c};c;Ufzl;}fFDo)_q3 zFDeMx{lX`EKeO;TzOdurs*zt32;eE~v9Q-H^v0WpHzZ;Kl;LX&`w=DF$)@@_etawI zbL$d`i{nl5bwK*Y*!-l4@;eLP+xx@aN`J8MUmPTY$@s8YbxlK?n(7v4U2)08VFCPz zpL~QxGK;9N2eZPS*{Ra&vFN4GzPnR(Ab_6Cwisa0UedWV*p4|qwzt@U9qpGp-^#E( zBX{%(oh{~?sM^hpC5Of1v+H9uI&Oen81b>I#pBq`-kMty%x4cD3oIVbp0+=)zq`<4 zFMDWSZhmhfP#a5@XEp~}!l;j>7RwC&#dbx0v3*CLf@uW_(I1E){U|LhGE-3~Eak#}1hUh?jV(8RFyrDT6 z^K&HOnUze=O_&wGwm8~&&6d~NX|rNNLZObaSc68QLu_7iT|;8j%*J?AY*?M4br8?C zc#hfP)0UC(MAFY%dFWFl1I9X)K&)ajdc6k2OwDjH^%1^7FR>q9O!0T78^o z&x>^FI!T%$KQB-~MRjxLo?jt#|c^xU=16f+#%G-qJb0byDHw z485p~tBK8wC+me6Ip_TVKj4Qxeq`}ue$rajyFi)ygi?w1TeEKcod@it`C>hL!Hlqc;OW<{^6-TRUjI~@f4q%{)xC%iGlJ!$n zyMrAImb?Sy#sa`gC41ls=4SIUiC&PG>r1A%NI^E+tD^%_#-t$`7^dJ(mplu{<3h(= z)X_TuXVrufJ0#b_?bS4hQXA4hOdU-J<^)F79%;P;QC~wsG-S zhl9>8DSl3Y%t?1Q@5#`>tqLkzx&!v&(%cEr)$paUeHq%X!r5yvHbX#ZXy-b-Zx|_C zi%&zl)A&ka8vANWL*E=@rpjF!`qxo9oJK!*yC6g;0rd%?-L>H&w zwi%0@9PmgpnfFQOmFX(pFUcndQ5r)1d_X~F>ekr(o1iy!+Y#>E&Oc(D;o12MGQ)G~ z5YgFLT>dkX|9-gO9z0xd5Ah#P!9DDdot@>7E%W+5!WE_oNS>mcbm^NPElbnghU_tm zEQ=TE{6bXejI`2rje}4@X8WP!NB*JYM}7t%Co|>UbcZ8Z*>a4mTLhwj>Rsl)G>4>{bqXf2DOikD8%FH&QIVpJSk>3Vl_*mo{4brzCgV`JJ<`($XyR1k!H?eM!<4o4CR(_x$2KN5y`0 z%tv^54!!Zb6I^b_&|w~mGqJVxX5(E8zQ^!3&yQ+k8h^F8-Z!Q!%!)MosTq~E$Y;AW z``45|gFty2rAy(Drg^d%hs<~xg7$SMwRbL$BBwlF5-u;JJzi1fZqd#5noxWlCD^L_ zZJTbXx10bo7fSWv0IQv<(T%C=BrTw_w6ho281S4b=m7UYbW{NxL`c8xP}@ zg{U&Q+0L(w_;mWj_ZY&sYwf22hrY%rW+OOK;6I&hdfUsb1P2`bMRUY9g`{*a6(nU z3C&WeYRc0*eJKR;8Ine&dFC=RCHbr>cf|b+aw6_9&rS0;8GVbgVO$!sm!e(idX5KV zqou5+rmRJdI_WWS0Vn8Y8{vG<(A{Rnqj0|Sos?1^GK$Qlm}!Kc35U;hy?>$YUkT?u zYKVJLj(sw-UpLJ+%8PGthH7*azC*3@WDXnld85X_~=FsJ;D9ns*+7Whk$u0n#@u(CphNC;`lKhA^ z=+SxX4Nbq;kTN@NRFqHao5jse6AVduMn<2Njm%T&IX{Kx1Wm+fe&)^Q=lbsUX5%_s!Y@^#cVGz(?GNNgu}2Syz5JTrAo70# CK>Bt7 diff --git a/prole-auth/target/classes/org/prole/auth/web/VerifyController.class b/prole-auth/target/classes/org/prole/auth/web/VerifyController.class index 93fb0cf0d3d425881858401f2a18054939e30cae..6301ee7a0d4a3f6245279ea2cb22b9753604e342 100644 GIT binary patch delta 2050 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 delta 905 zcmZva$x;(h6o$V}x|0yv0%90Kl~F7VArLTth!KKBK|}=&ii)5j1VM%{IpDa_C+MXw z;1;z)mkZ04W%&kH=|)}n0In@hcS3+FdvWjIcR1%e|GDqVfBL;&{=WYJ^zdxM`_i$; zONgkBMw3FFvF!94za3>&^&^^`u|Ca93$1S2bdJz&)Hu6c9XiK|8`I9Y;!e8UBy_qt zVSIBAxsp03Ic3Du{^CAPyXn_C!+`NZO}NhKoM+JZtk!xuxuA2Al<`OH>bj(JnL!0D zy)ZF5@8OEb%-T$P#Y-R8bVj+Z;GSLWU07OMS8#^wN4<11u5*K%#-dhH*}{a*ZQ2!z zGSkE9$;{%^s)s4#l@?H^6`G7vN964f?Uyo^#$gwwT7}_4;f|??s26Z_?WrYM!<$jYoO|3C10F~!tICHm!@B5PO|L~+{ZIaKSi=R@l02&r26rGnDj`G3>sf@^X_oYz30qif5|pK{apG0)be7%{OGGsVh2?Q zJ1wfIQKSs@PxY5gPDDn^nj+4G!V^8Eu!BWSe-Elm$_ZKcsZ~_!Z@&ECzlfrCzua-3 zX;RN#gMAkJIiNTAtCJdOGB{{)h-TgEf2j`ZAO51WRz+Y>G&&p!+d^xzXs1J01nN_| zIA+jo(L{h;VlZSe%!nf79$B0>`jk4in6kFgvw2pLI_AQA zov`CXqNCL+HNbt72Rt-*JH;@6bg+ul*$Wmnjhakjr%+V74UR70f%c2*Bx#++;q6*aGM!{ W-jP@%kb4q$c_Ni5*&LeZWc&v7tCSl6 delta 733 zcmZvaTTc^F6otRjPG?{kz)TQApel)64M9T)@lpg4l?WC^E)^o)s#cI9qv8$g6;VV@ z&a?l)22(0VUwkndAB@lb72}>lkOwC@=j^lB+H0?UGC#v}Vf)*kmCry2Z_@S`qtK?A zJq{uEDr$9QUQO9xNMQf7ZT8b{(cy4_PCXWi6m@aP;;_RJx)lw2!Pug|hGx`J-EP*G z^eW8GWO6(accnV&(8qB-Yj)-LbJAkKVNicJN6UsCV$>*%gclvRsboYDMs=IjtxoHC zt1f&-;S4wSO(!SfjRRhM+U6YREiO33xw!Phnpf)D(qMjTVEbc5@s!US^WvVDNKQsn zvB^`LIi6X}`xc(-bipsaz%Du0LmGc-B~3t!Bx4zq@Y3k;;5WHFWm4R%PkCkB9PS+g=HyUqDVm4 z$|knSQ&eq`3a?2e1vf-sYTe(oWWZZ*@D@vEfS5FBba#EEIp(&wt;_5#VPBTE3v7*B#RH)} Vl$9rxC$b*#LT