From abd059a9c5a993f7bab35e1cba9eefebbc018b80 Mon Sep 17 00:00:00 2001 From: "anh.phamtu" Date: Fri, 9 Aug 2024 14:03:07 +0700 Subject: [PATCH] MARP-434 auth by ssh keypair --- .github/workflows/ci.yml | 15 +- sftp-connector-product/README.md | 40 ++++- .../images/RebexTinySftpServer.exe.config.png | Bin 12428 -> 24712 bytes .../sftp/test/SftpProcessSSHTest.java | 144 ++++++++++++++++++ .../connector/sftp/test/SftpProcessTest.java | 3 +- sftp-connector/config/variables.yaml | 17 ++- sftp-connector/pom.xml | 4 +- .../processes/Sftp/SftpHelper.p.json | 6 +- .../sftp/service/SftpClientService.java | 15 +- 9 files changed, 230 insertions(+), 14 deletions(-) create mode 100644 sftp-connector-test/src_test/com/axonivy/connector/sftp/test/SftpProcessSSHTest.java diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 24865a5..0a3dc5a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,16 +33,29 @@ jobs: - name: Install and start SFTP run: | sudo apt install openssh-server + sudo sh -c 'echo "ChallengeResponseAuthentication no" >> /etc/ssh/sshd_config' + sudo sh -c 'echo "PasswordAuthentication no" >> /etc/ssh/sshd_config' sudo systemctl enable ssh sudo systemctl start ssh - + - name: Create a test user account run: | sshGroupRaw=$(getent group | grep ssh) sshGroup=${sshGroupRaw%:x*} echo "adding user to group ${sshGroup}" sudo useradd -s /bin/bash -d /home/usr -m -g ${sshGroup} -p $(echo pwd | openssl passwd -1 -stdin) usr + + ssh-keygen -t rsa -b 4096 -N "123456" -f ~/.ssh/sftptest + chmod -R 700 ~/.ssh/sftptest + chmod 600 ~/.ssh/sftptest.pub + sudo -u usr mkdir /home/usr/.ssh/ + sudo cat ~/.ssh/sftptest.pub >> /home/usr/.ssh/authorized_keys + sudo chown -R usr:${sshGroup} /home/usr/.ssh + sudo chmod -R 700 /home/usr/.ssh + sudo chmod 664 /home/usr/.ssh/authorized_keys + cp ~/.ssh/sftptest ${GITHUB_WORKSPACE}/sftp-connector-test/src_test/com/axonivy/connector/sftp/test/sftptest + - name: Setup Maven uses: stCarolas/setup-maven@v5 with: diff --git a/sftp-connector-product/README.md b/sftp-connector-product/README.md index 4caf2be..2304ede 100644 --- a/sftp-connector-product/README.md +++ b/sftp-connector-product/README.md @@ -53,6 +53,8 @@ Before starting the demo, please make sure to have an SSH/SFTP server on your co 1. Open the following settings in “RebexTinySftpServer.exe.config” with a text editor and update the following values: ![RebexTinySftpServer.exe.config](images/RebexTinySftpServer.exe.config.png) + \* In order to test the connector with SSH key pair, put the public key file to folder `c:/sshkey`. + 2. Open the `configuration/variables.yaml` in your Designer and update the following global variables: ``` @@ -62,7 +64,10 @@ Before starting the demo, please make sure to have an SSH/SFTP server on your co com.axonivy.connector.sftp.server: # The host name to the SFTP server host: 'localhost' - + + # Auth type to the SFPT server: password OR ssh + auth: 'password' + # The password to the SFTP server password: pwd @@ -74,7 +79,38 @@ Before starting the demo, please make sure to have an SSH/SFTP server on your co ``` -4. Save the changed settings. + Or in order to enable the connector with SSH keypair, update following global variables: + ``` + + Variables: + + com.axonivy.connector.sftp.server: + # The host name to the SFTP server + host: 'localhost' + + # Auth type to the SFPT server: password OR ssh + auth: 'ssh' + + # The password to the SFTP server + password: '' + + # The port number to the SFTP server + port: 22 + + # The username to the SFTP server + username: 'usr' + + # The ssh key string to SFTP server + # [secret private key] + secret_sshkey: | + YOUR PRIVATE KEY CONTENT HERE + + # The ssh key passphrase + secret_sshpassphrase: 'Your ssh key passphrase' + ``` + \* the private key is in pair of the public key put in step 1 + +3. Save the changed settings. ### Prerequisites: diff --git a/sftp-connector-product/images/RebexTinySftpServer.exe.config.png b/sftp-connector-product/images/RebexTinySftpServer.exe.config.png index 16dddd863d8a42ced456b8b3362e0abfab2969c1..f54c67ea9df288ddaa7ac5e23f5a0f45cebeb6f0 100644 GIT binary patch literal 24712 zcmXtf1y~gA_ckG+NJ}g!jnXXLD4i-w_b%PtB_be8H!PqaCEXz%OM`%PclQDkyZgcW z`+v`MZOmRX&(1t^&VBCtoHLQ{G!*flQ9VOLL&H~AlKX&$hQ9Ie{VOi^!~3!5y2Zl> zy4wfEH)!RfG8V)5?HupYLbIxFe9p`nqpJigGCKQNy@{7B+1uj{Vm zWa;i@=4yea=xSl%=w|KYZu|-N;Sdv)vYhNkZ{wo?9B;BcpK>QhN5^kk&|`RyhRZk7 z6=sb(+CCS(iq@<0)`O!kBPTPx`jhIcWxo@70kSVoFvz}8y&(%B`o7HaO)egdjQL5+ zHKKrKL^J~kOdwEFyg#|{|DGN+XwrU-+Li{M6mpxiJ+~N4+Dl1Gi>R}knJ;*yRiHL^ z#4isUOwv1C?F@~Gh>+3LjOBmWX^HRdZ?RV4ceQl>B^4EwxSX6=P+!b{2hi4Ns^>`2 zu9*TYhdYMTfqZt;oJTHG>A#MAANf$AmR`*;n<*gPzCM+NYBD&~nwpxTEdTWJ zzx)GO}>df$LloR#U{u{6Zb@m-&;Hsyb7n* z{}p^l^8Na270CTZ4S+rB33pW+PLzRg$h^oZ^Fq-+(I3zuy<4M4R2UeYiB$Lf3?vI% z!RL;EVx|H^@a|HGyhLe3x5bgUd374=yMJn+F`&;?M`j8uoDqcOqh$qq`<}s>H-2q*Dj_j;T-^t{=R0PQDy(={><9atOnO>bu6D?*3354BG2wR>V3*fZS04}pwXOz@!qS$jgODok;=5|z8s0QL;SEq7Gq6Fo4$ zRahWc4FbEmjA+hc*4KdjdJUmPZ-hxzMJASJ4FwO~|Gp!MX7&wD6jiKLc}*oH&*rx#Ezd-Aqn!f+Hz6N zKqE1R2l&aYU9SIA$`RdsiGaeGa7Eus5C_vd{aJ0Y`y!3E0YDxRQL~Z^MHD!h^PW+i zKDFB7X+tZG#nIN>k`jYMCtILoZy7cQ-B&FX_+q8H|2W_{$5W(ViUHxIt6Ay8!mWdK z1TVAc*0***{T#TkvY@IgEH!;}sMs27FJzE)7T^(+o3L%Eenr#B^?F0$-Lx zFGj+@_C)_^PtjC)S0o{S?b+pXM6c9i4z94Lm& zM{&OHTVXI3OISiR*2aDu@r?P+Q?-EPuUw;MUc3t~3ZFYFxtnYVi0a-BLXSepYsPL0 z;^|qGn+j_4m?Z{eBVNbei1#78a=Gtm^41tPC>gy`F9G8JC|24G=hf!*=UG5Gmfs*J zIpA!!nQ1B zYnVv&7?&OXfl9B9X^RfM`%UPKo!zyX`SYFtr)=dgkR9wF8pFr-jUc{fK`AA^cVwHU zT2e8PYp$&UXZEcVU(V<`XW`LK6Hf;CRtjh%<(L#vc+6`>^qf{2O7KDuU2$?((wkOx zOLn!+M&c_V>7rpWJt*jdG$3lI#TOv*O2g9wIl=Ax}WPrL0d4bA=Zqb9%Z0mW2Pf?7c{7zrZ#QyxBs_jF>CA{ywFfIaG*Rsrh<2 z(7Z4bIa=%=&`$Q25k#J!L|WW*UCs4yk4 zP1K3+mkab!@n4(qb@6Rco02CKN-z_%IzGu1w@FZ`wyYxANuN(>Q%WgdhkHji>oZ6G zW2sa~sTeg0RYo(!-eh)c~+cb;)Z zCKm5CTUGo9Ac0$p8kviS}3AS**j=?UD*49~C znEiriFiS9RYO=udFbGxv{0(r0IZ}mLm4Ec7mmXQk$nE%)f@W_&OHNG2_*ch!zh~W- zr1uMQcdrzro-f($jbMxq95{izMdJ5JZv18Xa?}Xr>5syJ;9>yOLpRHV5U=JdmZXNF zlROCrN5=Jwk3TU^MnJ|a-CtHbTI4lS2N>3AERIfw`NZC3nlEbbU%3Vuc-zny-Zl*k zXbZIZV=#d@MytECEYfzhQM3@>9cM?L$bo{Y7JK^zdO!B6#azK{GU%k|hpDw-WKmG;j@ zx`#U0*}()4|EgMZSQpxOhRT#ilWR^+e^Z~t34KD@wAtac_f{Cl%ZCG}WdL=$Xw2;J z-|lV>L@is@z6s}URN=Y!n;Kkbg54b*nAr~@|0AAOITD981IShSn=h{ox2Ffri#jR5 zun(agI49DmPv03jLaY*Hrojh6Q9zUCeCdfA!#+FkA=X=#4Ha^aPU-6k;+%X^V+||+ z{&Py#{?iF5g3D0juO^vkorlX116$cceJ^6F7)9)yOHka}J+{m3 z!z8xZtRYfoxJ;EqaV|LB-B^>HcKw`M*ptSliiGd)gnH2Ng0d2s>L)Eml@oHFSh|SE z-&rJ-(!L!{px2{SA75zWBc9bHacJsiH67=v(k!)Di7@W{(cvyyFvc(O`Ll;{YubhB z72!d~*}e3Y36OvQ53q1c(f!3c&-%RkgPzJo{ww}AgH;t68QZtG0Cqf_T&7A-Re|d2 z>NuT`pM8h;JU0QhcyoTR^Sq}%_rNUAiCH$*3i^I~cnu5%LmCK!h~!O}>^cpuC+S^I zDHPCF?y0PH;ITK0*Zf)Cet3ZQQ)pxDU2AV0?ZgNqZYV1&z67PZ(PJ*qa(HJchIqK& zihCuz;0!6@_;RU}sCLqXGpl8JDDX#ar!n|RYxG)3%Ww+_lh=)0lGMqxO{e+W-Zc_; z_T^q}!emPUR4p9P4Lb1)!CEXREL*n+EpdyviU#lK(2ieMn957sN-))xaR!&lQpMLS z21PEehy(#D8WP|8Q?xFrliaD0+==iT&MOaLX;Qi*YwHt*@wlwZg&*ONSEcD?j9qMrT_7RII0>tFW2b9>rHuD?KCm35 zh8bG?J^7Gu;s>$Ei&mV}xa48i+~EuxuFcvrn6CL;fF|7>`=mX5#ajEBs!0J*l=8Dw%D+35L*Kqb)RL&A8Z_Xx?l{yGn{WN^>+u0Mm)2Zis4Y zZihV`SkDIg#5d(bxofB(S7n=gu7>zKZ1awmM-ow1S9p`JL*b#OyCA`|9qpR=<`Fin za^8NCh#*;dbz`6Ewxul*I<*H zd9l-Xn~5e;(6a6hqK>WB&0;+#o9Y<@@j&uRk=@G8JH7~Ds$S{DEeRn z_M@hFAkl-2Vgq~N4i^0$9;Gpg_l*EUya98;=2iI14}DUG4(KEnCjBcVO+ei1dk%JX zcE}6E{zkwjADf6};^UQWV!ey_piv|VkRFWm~+!s@o{R-*t#-1SZZd-(NKu;WC-c>$uZE| z(cy{EpS39K@y3~uPbGIuoid_xGt$%Y-cJ~}n%D77*D+qQd_w^dVGSXi8^8dJ_^2!t z_vs3XVDS!wpdvNKQW@6a<2ler897fsj8$sL#` zIBoybHz?=XsQ*+{?k1qtrgOf(Ce)RR+A_ox7&_5^yjU0!wmeKQIlPMFHQih<(?yzZ zWqg&>NZQYMlD8zR@kgWB+)w7!3?Z6c~V5qI+MdUj|)h#8%p63j)PK#!t(I(k1{mL*0S z$z(}8^3SrI%YKx!NuPMDxK{0@7yu#Fd6(XYPqa|gq~RqVYqpu8ZFh(ptVXVH;34i3 zJbtD>bcxFoTYPj(U40AejO^Vb6-(za>}|(63ov`|_^|P3nqWgNVi&k0c>7asv5jdn zt>RD*aFhmH~5qG?KDJONv0$f<01vKFSBm7ik=xN390wwHLt%b*5MVM!;?VD zCqQKSHyw;uCKa$hFSv+3q7c-aZiM}kdvxbY0 z#z?$%=r#f#gT83D5TOfB-3!!)y3k;??cF+SkjNdq_{%Kb^&@Q1*AdfI&IkP;4&PY8;Ah<(4Gm@2+WmsHO zgOT+S)tKQknkU4?r{~)M=p$l#uKtA?oc66I8j$8&~ffVqm0J?p8S~3E*1Zerj^&baspRYm|{hDh_iZ?nw}~X`MAm zb(pxv(zgBwfKR=e|*G)Zlk~(8(%u$WLZ# zvD~34$1Q|#JhN5HQ~%!EXYZ*e0Re-XX5uHn1N>Fhm4o)6$)_oi7Jlo$J0vu4H<31d z%J2_@%&=024rYZegQg&^3tnX4s-GrCM=Hbs8U86~ z*o0-lY~QcrwFb66=JXxeBMM+Z2O8VRd&6$}M@H&&>AqUFf zU?Lap?_k}88@Hg?=yWG7odWcVXq;?X97qlt4JVM~5g%W4(V4x4Wn{a!0h_(0Z%hLD zxKLj7(lN_c*=G(0lj1kmMYlqiyf3AF`B%;y?6le$|93*TIS(u&+eKJ;=03U3$hT4u z^ccV&{P}YwBPbCpE-oJMb75+>D8Ob&rE3~q)o5Az7_52giKJqS<;VWbS=9-eNh8+7 z>E2ujw?{7h_5#h68)Xcvm=_lptBmPBShTF!-)>TXjt<>hPfJ#QAmNY7pM!;~;EYFr zsJ({t%^d)|1cW!b!tes8dk|-5XCTWFZvh#pmMW7yvd4A#LQ@Vlgl2x-ou&K<-eeR8 zlDUX`zLabqJfRdne_n@wCAo32GiC?&|2+-aG8ol4`CB3n)$FI}ENFWwamRT&uh_Bp zs-rmA>qpifD50XHd4qe%E*T2^1fKG{TxJjO@E;JH#sn8F5T>o&f&aL(g}y}YHT{)1 z>=7kG1IBdwb?H8Z`;YufIg@86F_-&Vbt07a8{7Lt@@0iAipArV^4VSmMmhxqQ=;Q& zVZ!+GTZJbjwbwI97hW2V`dDL!$)3vNI+S*7WVn#-+x~iQq!0ODmwwL+{@Aayo z_d<2bupfrNlnNyW18lVnbf~?6=d<~c=!?su%ekC{BAw1c9RC7b@31?aUW`og{XU5c zs!DN9)ZYSwW|_MyIL2y={EwowFP;YY=VT>^A{&z6Y}7c-_tA)Xb;wR`YQtV&={d!mzj+Ivmo6Lh9eSfW#lL)`jol$9!csmj^ z>E+PzU0Ch&<=Es;9rzBT*nNkgi~V;KIl4Gn1$1VNGa+HDTI z4B`^xo)ehd%%W}LnVV)l=7ITuG*wqJV=q+-_?dpMjm#U2r|C0+2Oi&(ZuU0NGb#Cw z2T$7DXGdSvl5Bt(U&I!NDpP;kpBOs9yxe}$=?X3GGqmuJ(kCkF<6MDhtTJyz>%jZ} z2^Kggk8jIh%AQLFe3$_mcSIO7+C93mbKm!V-O@HOdhv$7HSbR-sYjIVBrWLnb6t`K9kTh`gT^1Nb^s@C*P6EU|yK z(y!b{@^dlcP&VE0AbuwU1N!_nG)q9K#&y|2jA1rW*m<=G(UBQ7ZTj_5|I;Y)7b zEZhYx9mqeP$6|>LX`}+?TuX*rGZ?30NWIUU1YQ-|f-}MyNu&ceBsq6MAnyR5VBm8n zCZj*Sc6ygzm*5fMzjQdA89`dHqP?=QO$nRSWDBSlGJ_2cRB^hFhcC;s>vjP&6pcJshLA4Gc9BS$ z`V7-_q68&Zag>BK6PT5Y-h6*kzhPo3(4b^;u>`xYI&FLgQvb`BHK%Fu;$J_gqZ(H- zU`fSv`xfc3!L4v`pybT~aj%*KhDa)2x_5?|ujW6MP?(tEdse?oj0c@yIgd?1K;0L+ zi-Jb5jZ$^t-{fOOB7ItO#(ikmo@dbUu@So({tZlE=lDMN2NYw9JcOs^-^Z@ta4zTW zELHeJ`~!SpW1%n=?iO~EqkX&+;=__4zsn0h5 zeB$LI^Ejpy4jA}h6bKIvv!CYwRq}FefbXfYOx_knXUraRUKI1fJU^2Jbf zuI6x|uR3d;xs%E)YS|6{sq{!=Or5`F+kIf`BY)$C@$L$O#2@43LXppyH!e}Nks_Y| zzFikrK}Vcxx1w0kZc5albd1ZvPo8DYpr=)#d$*EnWuICOhdi>H7YgEC`+RkR z>tvG=sW!raw^}Qnrp*(;$4h%hd334)8yOLCVWtSjQ~5Ld_b}D+Kj8|p>nS0wvIjoQ z30m=#@fFzvczFpiH1ShVd=ij$ZS=rXA4#9}G$*t@NmD3@;n3lzs<;(tzQvxi^5bUA zV?>0zTU-TFqPu4ltH?BqP-&FLy3o{#^V+f$9dWIHxAh9qOYjzTSmX3$ z%uwqZD(c-s90?z8jh}56#9qj~ilzkI$@ehpAEBHLfNdV6$@qL}2@?3k$pxH=G1D-~ zMyCyLc^Fd+M}(%2_)qYAeO^E!o=S9?>2CK_wwb^Ap+AE$#6-AxK=e`DT(g==1tuyD%=K@QzR`VV|a3`-MqcmL!e}sOTauLHnVU~zeOlVkr z6Pq2Id3aj)$?wc^U6rz72g^UH87<27`uk{_fZ@r3%+*=4HYY%p!7k~tsCwI6UlN118!1aOnPV7cg z1zvBy_$jPyhtKiEdzf2A2GW}@mK9XiKr)nQ-18;WAkGzxZ)}DPY({~=*;9h-cjaED zPNU+DK_e@xQ3pY}TPBAaiwJAs70_?}2dbIK>v+8pczGO}&b@OS@Ko#KY9pa2>6SXDsyN>COo zJoQ|3CdUoAd3zSlx_GB@gUiav*~PS7KHjBp8PlIomW~HrzL`*q-6`n)s0ssif43?#)fgBk_sGxv2Vr7ZS7$^Qo~MRs`$myEg2$;!*XZ zHT~Ss@DqDqLbl%YmHoG}XCy7D_4p&S6ma-@??2NOR)HI+2EG1*>nXCp@9e|GbLJ_* zyf!|}1&z)1YL_ph<(OGCiB69n(X)?$fzgl0|*K#?#{+!>1%7a(4r!zQw{l7H`bdGJ&N zu|vN98pLXKjk<-fM&JW-|dpcCfqr1nu%Tb z*XqxWRUy|Nx#*VaEhx)VU9;S?@O-)XBaga?h2zX+-@R0nJg~jhg=OPcuA2Qy z6CR64bPdboN)rV@KCGzD?@H^6+DZEQhdC+O&Ews%nwuc7 zuH6aa>h=MzumN}&P+xjB(mPj*ePEFBT^)knOjWt=N~tIoGk`QN-$*dpde5G(lvtdw zIfVa;>Zu*CUY0lu3T1JgWty4Tl*kopvcCRzxpY3WpcNj0 z06Mpf@H_jBnNMY>*7d_wZ@?s%=jAs=CxYc>Z@*>Jkzxl7m!e=DEkVk?3Th4YsgW;t z#Orc|MOIddAD5A=bN+h!vLr$y9rA@0)wb;HPPfbkXv)u|+y^bnCi7U|5wxOwY3Vx$ zcj^M6eNKb~JDPo)Ef~>|iUu-}>mr-T?Ea5%mva;CDkHyK^GlO47PXH0U;5W@^v1{w zhp<_w7#=;Xez*!x4dOip1;3+=@4dEtM1;JrCF_SPnEBBy?k2tXme+rTsLN~QAGVl5 zthvM0i{X(7*|TKTz)%=HR5t4T4Vt6TiQIzVs5NHk5POC}GxcJ>?#tL}_<`&8VkIe% zWIMOI6|Z$MyY-U(EOCjHk8i*{(uuLU0LAC-<@G8Brmi3#e4oqlfCZYj{)a^ltGswI zrZZkcIS3zwU7+zG8yJ4o#)XI#bl7*=1^eUe=qH@5WecD3v+25??3f1z?_Bw?fn+PM z+`{h=S%W9q?Z(8l0e;-3T3sdmj%PdK-tV^DtaE-`nN(~r>E5(V zYgXQ;H~L;uNFV z@|`G&iQQqB+ro|8KTnTF?Y4H)7UeS#(6c#Qi_Vl!?s-cEmaPfgzMD&n=&$YyY+a+? zsXFRlQ?j?7v7C<7yC622vv$E29-mqo zThRTY;hASZ!~NxDJ)@Kd-IdUl=8xapG`)%o^qp7wPFojmV98;VXR?k}Z7vsL+mCo~r9JK>U*xVhEPdUIrc?ibNZQ7n-T;lYpU8 zphGhFy3*Xo`=Q(lGo4m1HZl^!;?9Jh1GDb-q>M4;uwf(34b_7j8L$r>ts~DfRjZA& z2#U%gZ#+g7#>(weiD8KsQL0}rlIcUrG}FVj6nvjpP1Q8D7RCUt4I2K;LPP4XfT#by z-tn=SOfbdG&u#`xxa2>hCRFdswckL7Tdp1TcvUrAVL19WwtQ~dU@r?ht^k6(V#c0W_qBQUxwoP%n zpT2WGnSMGu^DgSZai_+lsV;}f@Ob6m$0JnW#vc8K997M+ul#xotYYLrPxXakE>2)| zF7(lkZXLKf<`BV6`p$mPujxwl-2!_49oY_bUSy*1Dhdu^Q^BBM{&Q~f5 zi8FP`_qbzVLziE(5pfrQwNE$sa}SMe4)(29i=q29SnSQFEK8Yc_ei#Mq^lL|=Gt*a}WZ zZKY=KocrJ5{PgR&Y}4NSbTl(oG5UsSK|8eX0B0lzz5TpFAkX+AJ7!gRad(mYrQ2%% z4~wRZt%dk_k@OL-0zqq$lZNo9=39@#sw>YDLJA%l#pvi0#?sBsKe!LfusGGCU1r(eLb#nG68Ja3)~d$WSr{*wo3KL6#K z%!b#<2Q?gf6anIY0zm@UGI{h5X|Xj_cGV-TpY0lHN@)DZ8!&DW(v$`e&P9( z%4BVjhh5Gl8TH?U)DJBkI{G0DHYGJ8Tx1=8U1VC8X72ts>lZo)nbC!gHocCU$quCA zUMYz}$k++SGieM@e~Jj-Y&Y_HJymj|iaH>pJn+!B*~m`BXSOhvmEy@d3QPHtF4_d5b-yhj!9%jMhe9VnNw3au7T0Z2d z`ucy&L8;tcgNt%+sc4Ou2~V1z*dzFzd6m7iJzQkq2sJ<4q9!yrpR`^DpMF5FoZinI zjx8r~Ih>nH;;M`LleBe&^M#&dZRQ7w&(@( zotZMp5DvUwc)CY5qDdlGnSX7^jha#r_h&+TfU$uw{+k=nZTR{7|k}8~xm3^MoO8 zuxo0kGt^#1^-Gprd&!XG#AR4dBq`>h$FR|FiZWQN}ah=GG?4Z#RKz|q#l*#`Iz-WKp^wpZa3x}EC!Mv zL}Q9o9y30md#vM&me!&#t?@=S969Mz>@If~Zg3|I$O)ozMHqCpHAW|HTD(G%a*;OZ z8Wj^WZ^q6^Q;K@&{n9f#?0#%Z=6IK65>cJu>+rjj8L#Ro&U-V_|I+7c&6b?9o!=VZ zFVC2>3GUI+XFk=Da)LD048rB@~H z=7wfpk_@E($WHGhM@ojRDMkW(Ju#vv_3{CS@B4T+1N2Nuoz}U|n}%0QlVeU;X{SXM za;7zR9*Vp?{Lr#h0othq=~@v34TTgG_t5aJ!nCKROP8B1&q1us67#4<``aB=et|qj zhTin{F5f(mz>a(!^R!+&cBx;++>XT0*h{^k1NYhka7$9>va;AwZ#&_k`DX2!$?bqH z;{qwfno)UnVXtS#KW2uJA8SYhDb6j6}4ULoJXdMb-kK!dR zMT}@#5tbnFp`gE#k=>=)i5K5ZLp0q;TCKTxa{_+NS+1A!3azQYLbOc`B8rZFzqh|3 zPW{iPd`21*$EW5kIu`TYDIqBsm@#fN_w*W1SX-VeJn3+Q#AR=ozA%|q4{w7zd}lO0 z%?v3uXtp|h>9=xCbjqz+>l<7GcOJWEI^_hirUQ@g#jfAD9Am$(32E4N)|R!L7YUsK z!R6ae1kYVQXVy$*6WE*jJQUvFF5EbfEY5@|O#4y9rVp{(Y&B3GQO1|p;Orf$g>#O2 zOFgYG?U^-O#ilKcJLAu|FA}+QF+R~$$`)V%0nf9$i(Y5EuQBu*N(H22#ktT7y#(8* zbTe=KT3S9Awoi%+w(4{9?Bw9y{KAn#T#2o1EYH6H$6vL`bD&3{11%VINZ&GmQjx$3 zM7Lb!6(QMh-Fh_C8Yd=*gX%oZOQTE5c*J(oaOqobT& zC%S#CTE_qk`Cr=x`LP`=0Iu3cy0<~eFt#v{IvB`=`Y0wA&Ey{}eCAgLbK05uP5WJ~Gh`901eA;pX+-TfP4-ltB6- z0W?9EV1_vmZ&rMh>w=p_*E8X zWTC{sfy-y+p)!h?sE0Qsz zM}N?kY@&Y$*fIF_o9_G}N6?C{q0nlO$uY?qgnwlLK!V5+PEUHOx6&C-b+{#+YRpg zDQs}jL=nQ9(!gb?G@7+qY?%bY4) zYWU;?Fk~S3z%@K!&Y|Ibf7Of1xMf3%Eey4RtN#^YqwB6AB>*!rmjZ60@a)vybcrU; zFwFW8;6?Xy?(;pR;qb#9fV%Q+pu$Xbdv;5cG`_VUo#yX+9GnBiWnc<*PG0x)n|}}_ z9)5qY*|uhKnRS6qdI!0S7{~!ae_k50uK)qs{%=S;K8VZy+|%-#48S zE47x6c$+v9=S(l{GScde`{Amkif%cc(qco)LlWUZLsYpk^A(W`^FHA|1PgWgF;>*t zHTdB6&EM%arO6=gBj6_6+)Dpz@+U(kyD}9)@qnS#5zI_Q%Lu(v0h^hR1r3#$1#Z8S zu#)~%XD15UEkx0pc9w!!+HDgNkrF4m`EPNAhRadUG2b51J08(LQ>sOd;tXew3=ftX zw>NOSI97S?J2E@^U!leFq3mCB+RR??-8uC~J<)8p*$;%Xkt_r5L%UvImmds7a_)Av z<%F&&Mc{uq)bVxadd=*`0zBOk6COX#53x$NKEIkO7Q&u zDhP?~$^U7c&u<#vmOWOs9|nZ?Z^onkc~C&O-Qo}OWJ5QwzQ6{WoE83SK2RO>SPv`~ z1YtJ7XFfC4cfB7IvA#une)}Yh@A*xsxBLrd2@Gp0uJ-Ok&77lH^m==A0*Hg;miPzM zl8UB}Ko==MSUanu6Y;ys?mEzD@?Zb^zth(x!v9Iu(lP*{>R2O58o65FELQ*;cEFom z2Hz?t^ryhJ$*7T8aWQ{KlhksI+9SH4s2prVPsDeXe?KyfOc6W5#V`t(0L1+cZfJ=; z5mLrfROlZ*7{s=30#7Prm)v@fFFO&z}a{~rHx7%l|>i9Y$l&{x%# z3Mu^Pms3iDAzbRPUbL z(d-<-vN%x=rov%|z4D6R6#U2Z%3oSU6XfU_AfB- zTYR;@oPpFB+nu?(q<$0nwZSDsIuP9(F6tY*u_)`bJ_+2$tq=)~LbsLh{3Q|MTV+#V zeB}6YKfFVOtyMpzoeOUkk%;X76eBEs?w&PA(Q4Wh^HW}PweESV>3EIgs8Bp2p0mEd zO4X_^tB>v>t-cle1=<0}Z>hK={pXVSpAMoIrc#BK|dN;{34kCn;I>!>MSywGD0!@ge_2r`S4= zGP|L~k9Zwhpflw2Imei5=wz}af0F9E)jhU*>uMhyp0xr3+f4y2mmKb>1SA(cT4r7+ z96FX{JD>qfQYBiH9M=TDxC=^xww24dY*jO|&$^kF^2DI$J1NG>eXVIhJoh{rZ)U?a z9G+(Tv)jW+gO`R;AGR&#E2@Xd z`6=tO&kclp2QSGbEykIX(4Bw7lS1Jp@1v<)QJan2*?k76eB{wVm(v4 zgJ0;t>Ho8GXCvUW)VtpFqFq2vE)Om?o8$&pu8zHAYJAeCvDsJuk#}RX7RkuTV;M;h zzvg$kqmTTFpT@lTm@-4Zx_>Nd&#p@C-%!Cs@bZ-H^``t0rj}zpE!9zVjRdbU`PSt_ zZZ}D^B|$){IVBSOUsl7b#n~4`<27Y@d4J*dVUYF_yFZaylON&{Ua^R z-D=Nui~p3nCUgb4(z69Akbh38$nt2-@Bs8mF<&R%RxHA9EdZt9)xT2Zl~Y>F4h#lJ zzR$jcZ})Q!Ninp{-Cz`H_|t_il#^NrB;-F>1QqT0v|Vl73sfS+=Cn&t?B9{ftL2wY zi$T1UknD5waV^UnZQnsJf`?wF0Csea8TmC``I@$h9|S&ro(vU@ga0Rj9|(I*cAL3q zmMGh9pFJEh<|-2?qbP2MA;N?cIEZ^ZdH?|W-*;+$-UXmYfjtoAZ8g6{;mb*&CG!Qj zg@ zeC8R~UZsQ@*b_?YOA?k-^k{wnkDIoDVF_A)XkSnc!EDir>DGbD#-+&nSF85}5(pKn z%C#XRnAaZ4dzqw{9^yN=rQDo-{v^*y@(NQ(Kcav3uNJ{!yHiX4<;vQXW+H^bW!K$V z!#4K|Y26J9Tb+Ee7;*d&F@*WqdVAn#^6V1&juV+SenzFuwDVd;^JcU&8AAP9sFDP8i$CVMTFwz=&>kcJiN{i0m`NQGc9WfSYLsdz~Dtn%kmK=bj;} zY?thJzTa2aGSrnmUn{dM6d<6mW(tQ;v~&}hTJ&E+sJVRWj_M!+bW>aB0K4X$A zW|dc*Y#M3@&GyVhfxa*`I}kHn@8e<%(Itv+X&StBSFr*L#CMbXXAl_Wo}E312S#q8 zs9h;z$*_u-R!k9peCRC@?$EWHmgR&M(59gLhDG1G(T3y<;#HncN?BcoT*rX9)EH@c zr>@4!-;!!65}gmQ3{_F>1KfLz^>lpgMMUnPI*0ATb9sf$v}%_ zH3g1k@nL$O^+6Xa{sh^tr5r!^y~D1aJb=#M!67DVMVqB-G9lR^lZtysR)7bVE)4wX zJ^?f))tB5TVXmW8C;-YwX~S*X`}ybhrjQwC?eaK^in_9lDZJm)N1gF%^7pW)`*8X4 z|IFF=!rT)Aee2as;GYzpLhSu)_)N@_?`bB3Qu_EDrb3~1lniwRR%(pXa7cQ=9st!^ zZY7}uk6k)fY;#s)D|gMUL-NeQ>?fPekt0gBPnk|NHe5D*TtAZBLG;>uNb=o`-M72 zN=ws+DXPAs_^+Y7zUnHw>i*Yd5%F`l3)&`q?pupZXa-EFIE+Awl%0JNgAIRYFO^yH zmEOwA|7+~p@y%eEwXSsnGS3~wUdV2E6-k;azV01CQm>LD$ta=cY6DCI}l5;OhNY>%nJzTJd! z9)=X>AkuRt>7HQbBlG}XOy>!D#)gum>@fEurW!-d_G5OX)br%w>zSPTU#~ z-)VxvkKWC`n5X|WCXD!Mom9+unIVa4y#3eO6E~GheS+!(YBUlsx;!ztWU^s_4*Tbd z;Qyo7@9>TZ>yUNZ@)U>JG^T)gUvu;6yX(i6x_V8gF20Yz{^`z5DgoD0CSD{)NwPD2 z0^zeY&O~e6alWjraWtF3$}@k#o>>t)-xbjyWea6gTDpSCaF9v-2X%ImsOHy0cv&TL z0i*enF%r36uym*WB}^PFEx-_3%j&UpCGqiBAA`|W3G`nAWx3J)v=7+2*+zU&2~x|L z0Ndn`-*Y-f-qCozmigy91&EDwJP{msvuRAKpUro6j(A=WksJHKe`coD6=4uH+&9?T zKBmVyy~{etv)u}svUOTPkq?q7k*{w@`iUIhi@0RHuQ{l>EOP}GNb0p}^aolub_o?* zg{k)9ntWKq48 zaSzkL_O^(B1)FO?PYh4~G`DXC9IUDI#Go7w)+wsGDo+AMUSLBcz?b(MqFxp_*dx$CnO9J-D6^z+9)Pt6FKx3kZf2o=)!i}GG%zADp3E+7 zVm5DUR!iM;^>jRQ`zjshE2mAU!NqTc5wwU8on_AaJ+XILr8K~@lwU{Cl%L`kqiumc zw>1+!NY12~J8U)l^_>0@H!9gDn=c1H7Zu{$RXsYZFIxmUmE$Fi@O&c8Cbc6NncX{NRM_0^$m};u+MT95bjd*38xRb? zI-6QY_jDPk@4-ReK@@XKDYfZSjSEm%w!vkmt_|?hj&TQe^yD*{@b6Eulk9JzOIJ>u zpu`WnQblSpx=BkF9KepG2*5PsX_wU#2-=HdGe4`bkp13}n$JA;u6y#b9Rrg^;C6N; zrQ@V>ykSY)IP1#Iyn_aZdHI5XE(Xh$T=Wv%=15?S<5)5~YI*n;$^NKgMAlZ3k|`zH z6DOJpmaR!_;fJK)*_R%#yy4B ztIl>V4BQ6e#d|q|U5ywOoeeNWR%-ASZx{M+f6+;a(exHWTOA5ym$cVud3Lx`z zue&ND_(Dpn4L{%2$?PPAmtFl#kF0AUJ@=%hRTz7>O9tMm`u3(2(=^nkZVL9~vE$|4 zdXmF(>R3zq%C{qtd?I&PH_3Q@Pb1|ikEg~IJL)mVyJ4eN2t)S%bemL?Lcq^vXN_%* z%2*GDO6Y{EnVY?3MwF!L{=4SZ9$)l-r4b6@J9=&Uk4O_*6^Zg!7w_7np9`Q?YXK=k?F}|?1>JD$nC&;D*l9@(hxH;ro_ABuYS|fua!{Pg2BloQFDWVacTw;-j$& zaIyv%v7(Ev5D2KPngUBfFg$NNsQM^ez>UWk7hL)dCAU%g5{F zVKeI8j7wzCI_SA7gfmr$iRXV4^ZNv~qFd)In_1)B{pl6ZuU%<{pVW+-py+Z+?y+Bz z5_>>kvZBRKIP1Svof{X^xmxllB&k`h{v&TsH_yv}%&x))M^)BwvJdIt(hNj>CT!&v zy!V!Eo5H}7bf0rzGb~;5XaGel%z^eVJBJ#j6a5APBTFP8y=6(QvK9M^*XP?J@;K7W z$M1X4J`kJfU^Kytw3h>aOm_vY^~mfak!3mbAKSa%wyW)g;B^d?Y%6FLciFNaa#@Dul}vO!}x? zm#meM=r4vNLMTFGTGtYr3b2(f7Cq@qit0a920Ya^E`*A>D&Sf}ZTf#*_e6e41)HIk z%@*ibloFjy%}_3BdvwKHYWq402`}wUs7opy9k2ME6n+TWJTwUJnCT1x3M5xOF7i%m z67!o&@#9aFg+pzts4NQgh^!uBV_=Kc>e?9h6UMZiGpMDImcms@Bqf%vg2_hQp*OD|BHbN1;qKyR)c&&Jp&ZIyu3;h&qz zxDF6Q_XXZhahn!^pvzV#@x+KFM6hxJOsT0QeRg^FH_r^{XeH9Re&#XXmlJ3;}WZD9*Uj^WtN;+44M2bPwta+PP zOZ)Zj3KP`NB%*WPYP7rb_9!tSD?oY}(D+_8xXYVGc|Qeh6ZTNDYq zCY_$QI%UuV-+`oAnW-;2j>Y$U0;vIC&H46R(!YrA-+(5Jb&yUDYVTRsr?CY?`NSc} z{=nfVNQX>*)+=~T+kM~3+}o?ABeNnK&3$p125Fn{l^|kszg$j zm@wfN*&VtS0qH7Cxzh-hWFf0`YHys`nbwSZl_;$1c@p3M)9qH;Ug%uPy+Avl!cecm zJoeSIPT~8R5)VRZ_Or-<&lEz0U2D&cbZ?Et=ZA`Ml@etVzxIR-KU2Y2fa`u?c*QL- z)RNiTY>p^EJ{_C2xWa6&-1yi(3MR;BDEoFc>JPt`NO#|Ibn36|#Jd6ol7L{au$~r) zCN8&tHZkz^fMKcROn;zqTurZ2VxemeGziV7B)|3bN76j?dMI7Z$g$dd(JZv3|qNnn`-nS)ygI)>=#5}pu zhR^Get|j~`VRt*0gB)&zzph$c-~c{{QymzTT0JAvR|RFI9p6V*Y8I1TQ~~l9{Lall|O>Ujh2DbRI(T@U;eE?{mAwmYxUB4 z{T3I(b80dCSDeT;^ZYZJLVXFHulqY(=*O+25#>Q@&gxAo04tZ&14tIEwW3b)9!d`yjwl1i%TQz<9oo z{J!|MYa7@*3K>`Q5}r9+uvspfw}W_>viYQC)Mg{x$$vW_2I zAbiGuFiqgKJ-0Y!bCIVT4J4D+!!e(uCvlQ6N94dMnx+tWUMoPbhVVRU3VpWk&_qS_ zn5=b|D0><8Ga%>K>Rn$L=(j6LV;mixz1A5L%t;Y+%d-oS(U$c>D z=#>k?=_v)>*2lVR%%5CnYowU~ET0`QvYOF34)woMB+z=GL-aF?@)lABXwH>@+oo*M zV$U%=M`>B_WjoVBLkB?P(aE z^wA!QLQUp@|1(9J^itl{92cC%t?rtL9VRB2|Dc94&={&Vc zCgvWF;E!%YFx)fIt370vhIC>w2X%(_SGe$How5no!$0hz3kMYo1&U&=07d^?dK1rA zVQ(;8WNjJ+!AvVl)UT5G5bLCS42IX43Cx`nt8q&&0;RjWI$t`{|HG}cg1Zc{4%SQ# z@9gHMaQ)Nb?%{ez`W>T5_2fUd%N-C}-{vj9s>!;fxdzqTWUc-^F(Y4X7kn?dW|@*p zeuUq}EV`5(%WOEV3sIb)M}5anoidy_Yg5>sPOVEa_Mpp-#a`^OSsfBHJ%$TKJ`;-? z!bb2pT$8DYRY~;M-Lrd{nzN(O{EAR@M9o}K_!|5Ln zyh^FG)lD6ucFdzp5<8CC$(au;KTv&BUB+BRXuLrAwrhyNDp%?L1dEID_eezlLRY7U zKlP<>@ZEHz&(kKXg%Wnv7cPq3AlMA6HSeHy_2o!Ps@Fd zSqp}^V6k4o`6bS@VIoZV3hy3Fc~Wf+fQ*nsIdXNC9h_VkfS@sg!+=9#1a0@BN`rY0 zHl#(Lfj8EiY%B{GR6+Jd=b%FZ?+X&0o-ws|AD$(JOQT#qMkt~lHT~`|MZRwBbU)%O zt#;E`bE+G8v|bjK9$N?PY<^d);onGIBm#x+?WB=y^u+<53zVW;OCxqc-G2h6UDnWd zZg)*`_vf9xOk_w^1T#Or+gcm_kX566&L1!Zikzz@9kN?|`~hQ=+LZD;2?{c!#>yj% z`8N1$-OkpO>7;KT8F+xq|BT3`*Ty6&(b%`GKF=aXQ{15^Y`+y-wcOLdm*XY$<#MPA zUl^}L%#j5tE5u=;@hjibx#9degh!Z@rIbOtNwEeJ5Ia%mmlq@XwV=~~fc!Y~<&uo1 zRp`?T*qeDR(c!z-Pfpr1qWU-JsRB%YixZ7lO0EM?j4OJ~Ge~4{y*TGpa*}~X>B&->L{+WR<{t-$V8DCI=Q(wbuYrEm=+ z;trjxBC~6mzX949ey|+XD^@m&3H--ty=z&42-=nqVjfCV_c2ZqvA30tFP?RRn&7f? zWQj1T?R=y{5CWwW7SB))f64;Ni5>?oFF5Lnu^SdC_0ZUUi|Y9pQ^;4L?0K-(9x=H5KU3V$EI7=bGWhvtg-O5GcKbPc$NPeYm{R zL&zLfzAg9wz9jFHIOp#~;moO0hIp*d_xpwyUOedZe<*V$wP)$(cuxF%%P-ybEY1!RuELrzz5$+)e*W#|@%!Gi z=G8|!1MLjO6Zm_WxP7b()biyP_oxLR6ruXQ?cbUGm%F)$urm>z=Z;&Xp*l3HPsh+7EJW{v(`jT ziLlBhgDu<#+kOv2zFy zk4r6AvZVJSdM_~kU=&#Nl__8aWr2P8c=a1npO>BHxN&|Ev#U?|+P&KLNo+&c^6ZyD z6W;?0cpJG+yB8F?D%q9O8^WTgqv1p+1nDcMywL4k?D`S%vCv1Q_O5=_1K}S*`ycyZ zx@jURPAAi8GGB0Jdzs!FnwJNOnTwN5eW|4leN5K0o*yJ0OxQ(qtj*<^Fo= za>ETT8xgCj(efD$N_l%JqOGXqtES|H|5ZnKh-A4!RJhi$$uAO~B5V~>E10rVV=QP@ z>`mM75;l=;T!!@R`$4WC^?iZI`0ycN;m>;%Bfry-cH7P_o39%X`kyGju();B@d2!* zII|YYs51Q{^6@M>HJxu@F`t-gXT8i>G+}(L!T~7`;C?JDAcFozt&)H;N$|(#aM&LS zjYH=i&9aO(m%3|!+RQq@PSBOg&Oou(c}`l0BaJ z-`T}}YSFHJZESY4~b6W<<|7q$U;ocFzB?E#=I< zN<=KvkX_JIO2G2hxQ5Yjm|@0!NUtQ1c?6^v_BbDtWq%N;cCoh!){-G`;8Rp+w@05Q zCKliyaiQG*rlEqm5r1{m4B*J^{23NdS zDO^VNDB&fnDr8PyMbEtY@L5{$qTWFuD8+JpE!;8u74ocX^GA?cD!`bEjHQ0w9r9^2 z_9@ANI~Q|{vt!hGEIfnZZz^M%O>l=Z!b(HHQpw}cIR_#PV0c6o;6C{fK6sLf^K;T( z{gTlI7UgwPv|uq-dY$pOX`8;3j( z!r_=bA2(e;e?jLt8(&fgoD!nKtRlTj7KhSh@GQCAsgioN^|s)e^jM0zaJ1c4rPgWBm23BE zL5i-EdC2_^(PO9L@ZI^PoeEV?k|(6MrK)gF_F!gdo_Z-5Y~&sp=(D4i=oYi|&YJs% z9EYKNrHbO9kw1R83jgNWS(SKVQw6V2;XJ;mQ#-R0ao{ z8OSui3Rkpk)(J0r;I9#At6mFUlEMy-QhEdRd*4XRH0j`V94EFmzA&C_MOeP$0&v*2 zZv6;BYiYYboaP+P;Sr0^kL){NG$HYOWYklGw`NY`N@Lp2+Op_q23N6`9q_i6VaR`1 z2#n7Gt3=rUP)0yOjqHIO5utdsfvVRTp5c$8`|gRT=g=DNE2hMx0rU=uut&qu(R)JX zwoM3AT(+X3l-zaN*r|2E${GZ-%n~f{m2<9D->3KO;`!qK*yz(d;8SV|pURRel#%o^ z=~kPUqbf1!kI_^+RORXP!TIve+;KlQCo|VP&2cELiE143n2W)3S>E`O+m{Vb%=!fJ z)uZ8lukNem^56TQD;^ZmbU2{$GuP_O3e0hjv7DcSpkzCTvv;cC%zNUhiG+)}SMpB~ znns~iJ77pW9^Ad$kvk`Q&G`74%7~rv8DyjPx};+5PDQw*MA8M%F}tdzzI*%p3n4|+ z0)6IM&^2Nv??!T$;si`e)uKYT8FLkXlS`+b7n=qCZtJPikDphxHrT^x0IGTOp_4wl zxhpo`#}_@VyOyVeoiu%1#*U*%6~DZlSY2s0Y{57D3Wge2_eu8nCQa?3EEd)=-PdbDFK~MhsZ5* zF`r%*T7S{i){LVm=M&6P1jGw;DBo9FOjFuH_eV(n7vwvS0m;)@d)VN6xZpmm*0-PW zyKSeq_4-4W!8t2Svnv-?zcE;nA44hjU(v`~*jH(t{{#IuWc{XG{yywbjKYZ4<&IPv za$f#@V|9-91mv5oasEO%c9c48WlXmGyO)BxHiq?I@K0U((7{xQ9sN942JhIz@^vOr zS31luCNCZrC;vQ7hQgM8!&$xrZE6Pw%DK3>T)cLz+`Fu#r_QC7l0VF_aceVL(PY6d_!>!sEBi;lMpEC=*#35sqx@@8DAX^3LnN>cdn&T% zp+{c~O??AJR%W3_4U?@kH@Ee(j^EgrcL4x8%mk2Ie{!6;(BG7Qa%#_9!DH4lTpFvn&MxY&DF^(j z6u?f6hDsOKx(OGAXe8^Xer6a;WAO(_@KDyNr*tb0VyK3Rrirnmzq~#$jAg_XsRhs_3=a*v2>y7)pqmk#)w}jadktj z9*o6Ks$1^U2{WTvXv`}j*8Q%mkBrZ1*~AyxccXggug{~s1q0@TjgU|U`n*?HC)|AB z8sKM!XD6%h=%jiPY{|Dwj0t3rHBQ~RV8|M8>S6n2L!lH+Yijx{m*f+1%)kJ|r1b8C zdP{&;?^k2e>3&mmEMPAOA)%Zv?@_v0ovoGlE-_Mr2=3@XZ6GjwxOYX59ov3CXmupsi0ukGKj^Hw+K8e9G3XjNM1`hGY765_Wvy8T{ z&hWLblen64rCFD@CAhl&amO{~lY|N7Sc=;2gT05RNA*lQNE zb9r%|?+2)OWdFWas4HmxoqDSFFcR%>92RCqNMGBa3VVOGW$+#8?Q$llqmCnbe|;ObIEqXtY!1sicbRu(*A9v9U16 zIltr0b5?Pp-LQ8w%}=I)5N zoM6qw2$Cn@vvtNm)?|%1=US%uo1|;i1{bps+%N$=ILIUv_;T~M~R4p*sYB2Csg z30Oo?Hn17jG+c1Lc#as!h8W5tV&8=>zl2j@fzb!6rn+-#6dkSp;zr8!?zZhfdgHvw z%AT^+sEn~D-VFQ!f8bOf#Si-|t6_S6kh`U%-bt3u$X95RdTfA-zM+Zg7UyT^!LR_? zPw{efA_(3pP^``OiNI~kFJYCp-}6B7jU|T9YB#2o9|r?F($Cr(L=6Y+f)BG`DvcIj zOey-rdb9 z<5Wds*T}A!gXD**UE1M%Eur@d6?leOf+g0SG26TYlPCew=v{JZ z-=P?8rp)C{>s76-=~Be$>}d)~&w^JK=pY9?)w@k%&&sAtz)PD;uTjNxs3{B_OgC>o{q@&|$bIDbp< zZxZA`DIG?Fro%kqCZUk3Xu3bj!H2G?35@e6TlN;5lL`DIGRl;|ICuV(ep7@;2mZa` zn=qW@DaoH(9dn=`N8jr&a5m5FJenv+Bl&QjTSzHdb{1vBlFUUDl`EzA%M~vS9Op2P z^Pxf^H{tpTGgr%&Uxtn<77~_^ooHl19DV!VaJD{&DI>X!EYz!#a00dg0&a5H%`3`q zz)oV;eGcAx-iaxA>w2K9QMyntlTecNi+;Z^Jg16dX&BHUaPK7;_@ZS;i>ZNx8n^Oe z5o5^o+2hT<7yFu_4-WF6Pdow%FjB9~0Zcv-%r{DxrVLUW@D~@K<@o&d-p95?3bli1 z8zxy!zphnHMU7@sMb1oJ(Y(9VTOObFd&q)ZS8ZwBjkSaNLhTZxv&TnuzVIq_bRMWF7j8CL6`Sg+l%(0&jZ7d90^vb?XMQ~RHGXk$8AZ+)!4;=*Z ze?IoY$tMLeFlJ-}onx?9@e4*W>K;BN1^uKqq1R!4k3llFV({A~d>AT!q5Iy9WFV%x zmJ)*JLiK5MHt@(M-UUs^&H?qqQhK!#UB{Ls>??9ECwH<&mlg$-#SiM2Lj)3CIgo-O z`+@`zfvh}CU$vfNs#|}h3BZ1%fpA~hTrz&D_{fiG#TmkO&KYm3T2$-0jY`cj^Z=&- zBd#KlHw(=NIF)z%jRZGrf07P`oP{W$FC|s=Z0&C#71N@^JZQEflHDcLSe1(v6#C{A zE6ik)9f-LCHdBhAwPxSKT?Hmtn{Yh&SuqZvGUtf-HS-Zgmj-6oJ^J|R?jHwKuKMg7kl;u59 z&#C|M%BuHg%fm+bgOKB+{3pI_4P=Ddrub<~w7MGYL)3?#pDmrX`|Oykn#F%t)%2~t zFTIs7J$JSd>Wv;FRugN{Cw{BM1lBp3UBJUU7XV5IyL$=tM}-Bv(#MS0%{q?v zi(WTNlBfM>yD0;JAtC~7r=_~TvB$ut9x%e?aCT!i0s4c129GW(W-&Oic?1oudGmG7NUqNK+8h22bZ{pJ z)nBKKAmskab6RN@;KVM!VSS9eY05VSn2P^{w3@$Aku{3^c=t|8SdzgjjzpeBErGkL zijJScOiCcIaVIql*p%3_Qe$etRLJwJleCHT{K01AAEzQ>)F`=BnC^fNnMMN)Z%jT+ zj}@$n!7+4vlbpG@6XCip4YkV*%mqe|Go7=?6Ek?Z$D&iCE=Bng4kTr_Zl5n#5t1la zH^~m2y*7Uts#3L*Bn5v{d?-6*Q6KW92$sZ-SYYY5d_iCR`mV7u{ISH;jXmc27{pf& zUA8nq5ZU7GMSc#=GE#!_mVi`Q_m|Db`0h#uT|j)Qdxdp9iivRzslYuzls(u#Xx=J$@aikkhmL&k z5}^{EG^VwM+Bb9fUIdG*VhlN7yf0>nin2ZL5>z{AcT`lx7j2_wgapec`4-a-JJu-Z zH{Lh1QXtmrtcL}qQh^BesZ%O&LRO>?2|h#LSLW>L$tYt307WT4v6|sk1B$-Sz-1(~ z1}rlS$SK1@Y@j~SfkLgxU4YK+gOY(NuflWz8wxzOiDIBoaVA6B;Yd(c1;d}Uh=jh6 zhL|8|dqGwmvumeWlQ@uW3R&_PH&JPu1Zl)&)C}2Cz$6`}11Ka0$NXGEW&M;`^vM^? z=@*Q#t+ftuVJaJ0I^eu^rN@76igaCX_yKhpF9Y4C2+lxHOhE^v=5u;sO_a(r?s!u2fz^KTG?jv5=mlhCB-NvwsFSBt^Xf7jpYy zGhw^LDKQ(wFPNvL>6=TyFZ3o2>vdLHMo-d5pnyD}ildt$WnEP45lZo?EEv-gNcRkN zjn~^z@T#R|!CK4yu_@A6#e|1v!{K+M$ZHl1ojS=+ zR+iFkO;-$P6RciJQ=J><@1-n~AuOZGuewxkb}SOAZptj!Kl(9ugrW@Kmw2;NeSibx zav*|htNY4ZwMeK>82YsxrWj*D-LX{pwP1c_wf+DP0>i@DrM>}#8(?U3ay_=8j{uDw zruvL;m(e@wa{1F?uD#ZUz?d!nMFh@>{}Cm0`2U58|4lml3mJ+C$UoWfXa6D*uN8*w zlL}W(DD$DC&=3RFxCa)DrQ|Lxy;*EwCamWAqVEPx(qmJSlbzyDlgrNbithkGij2^i zQMd#U^FFlTlkaj7n$l@gGT5sIWw9X_&tcJT4!a&HB3Q1I9m}F0>%p%fumUZtPdU&) z9E0EWTbaN&ySw|{p|N|^Fp@F|7(#k-8-xXt;`B0}#o;7;7e~fB#jxoxd40a%%{Yu4 zD}e6zdlW;rLQR4QrJ)E(#4iCT0;On__fX^D_rVu2F-lOy7h*K>6^k7|jzWG@i3tov z#Qxx9V%N4IWE(F;Uy+uokiU$EQjqzbZ|Vli=^z2w)MI<~A1xKVrf9y+GZCKMA+7AQjf`V{*`NyV2LqqDdEC#o_0v!X@d;(b8_wwt> zg)ouq`;x0B8V$nZ=;2m1KE(fMXH{3Y|LAr!GP;NW7J>bmlCmKJ^@ z7;~>6!+7_5kHSYf>28K$A!B(Kmrt}lht4wLS*lQAF`bsYUb1&Df#mNPWL5+KnR+4^ zzeouCh*kiM>-MtDety#M@C>b+rhcTzv+K8gUqYL9iAo4ct6RRff9>kMc!8D8wY+@@ zo_lKLhQmCR8Xx|!k>2;Zzy39|sYv18E(gcsPeUdi$>*J6vE|vYY4y2tY>{*Ehf@i$ zTuC}g?j9q}E72pz(+x`^ozv>p&WrRz(nCUigOBs$6=+0l7hdcWp|up@XZ~Q^TJ#sP zuR<~Oqbsk%B?JF<0EmU&QK&LrAw#?05Uq80XE zD^=Qe+N|bi)oJ(r%Gof>gCfR7@X(>JpC^UWp9ezvArshtn)s3E-V{T&{3oc;81mu5VU%soN)jY5 z)AXjcX@=R6sdw%v zS>*=H->Lup45rfmHWrOE)CbRp)%QKKQa+z}pf+fj5~YYWzxI4cC=u9U1^E(Bibjgg zvkddA=&@HXuQ&1hS`uf^Z`)wG|0XpmR=~Zk`=wt35;zbCtVSc5YA$}-4Znq8$WVqe zeKB*hh9hMGN9Gi$i3c2Hf0;&{WCi0MpXS=CZDF^hw%nz-q(&&xU0tXEc% zP(+;(eNJ&4SOViXke#zy=MBraQ=m zB0e3pFXMaP^F&rfZ%X>cz(|Va;E>>X#nK!o3oLM^oKla|c|~~?`Ceb~eAC|{@+HCn z9(|Ar6?*%g3oHYv3>c=9Cs8|yASl2`HJBpmqytE(A*n>FE};Y9p<$%Yqczgdt~@Aq z7G8#4`mbcaFOtz{#YZ{N^3Jczc~IryupEcTMz$w)V=tT75;|7okx=$41s=F+5jWm| zFb9lXq=N+seJTk1+=Yefu`l+W&tz2Gc1?|jtbGrzvh3M*p@-1dK3`BOy({(#k{gdP z=ouPhLRKj{x@!)ZyhGzYOsAU$DG*oJsRTo~E#8A6?}MF6nv&M2y6H#oxidC4IaWpW z*&gQY`bK9emp0EoErb=vjqisvDTNrk zX{$^zUpU=YiaB2N$Bb|MR}BcPiL;)iq*&aoU&W)`QCW+;-Y+dzl-da8kL>gliX$uS zefR3=<1(6h(sXen(Y87k&6w55LZZC`kV$z1L|sM)jXM7+*a2pX_GN zU!Cl8i{u66g8=|n>OW7B$qrM8Cj{5vz+}!Z!dk zl9|ZDq==Rfy!4$%&v5eTa_+nkn|^E5OLubxM8d@W!6)c*_|S0Q6g#e_W1prG2Z1Cg zSx|%jO$Z5IZq@hXa^Y9LaQf^W$zSps2siV@S(M|QPtaL{GCM3v(7H!MI(%KzuuLAf z?pFQXR6SpMF(4wUW?gEzS}bIlrQsf1q1aGEHMrU|hQ0K|hxP1VTDPYO4L{|(e2zM@ zY}E~fM>nFrk0R^Q@`jXLhsX6tRR-z`pN4+!Y5NX`sWIyHu~vA@_{)6TctEJC=6KB8 zfE-hWM>{QS>U;`+;S?HIJ2W1oOPCcHHcRjpz2ml{G zxb?GW=wWZ@CkRH%Gqnv+I!~jun)`b7FHh)t2t43 zx9~7NJN*8`F2?nh(q@mWM)zJ2)ooBFM^D9wvYPCcDbsGVNeH96GvKu7W?4YmQtj;U)aIQy9#fQlG1>-Fg@;c3A=4#!c z`18<_!*s&(HI@L3?G{I%5+0%gL|o@oi5m{;7fJPVg3_2@0o#0nxR2*BWQ9@4nM5{FprYuvRUTc~cih}wcH)nB$ z3N&;Lj)&U`zKIBXwUELAsbX)Gi%U?Z#`apzZR=D6u@iz6E6;Fs9Q?c@kLH3rw^z11 zPyC2{fy>b2_=?p3Wm>#Q8SR&v;=k)RvdpO{BN!&!f;dcFcP@pibgePypm%RSr%2rC z`;{ealXHjG{P7ygk%6pyk&Z%pIbJDJpshdBg_|F@mVwL^arst0<^I^kU_Wvhg}f!8 zC|5dm)+uMv^O*L-M_}&srU) zF%}8VNwvMu=KZWrYX#?0K-gr_P;1DHAu))bFLxFstqoF9P2h6n6wZ4cGqyYnVVhc6 zweER#EW`)md&^RMg#`KnBj|6_KY<}rp9h0Touf3XQz%BKpX_IWF>@j@EW0HC|j>0o5+*y|4i-o^D@Aq>h6(5MDwj*a$@Bc8!X zdH|FrJ)OkGVklO zasdK#S3cOXO?+r_a8PZQ zV`s|z%0oRNM0`u}T_FDfZ=S7iUb!*FZtAlq657wxu|cFn zl%KI4Va!5+U_Z9Os27H?&isV#Vthq+G5(()JzR(xjHT|#@V#*K#``mS<3ue}`{M!z z4$dXDWhdNF-2bm<=>K#w0Rfa*zW>mv7XGW?V`doY$f064dBLIqZYT}v#nUu`4eT$6 zw=^5SFM;i7G{cLl#m6UX7AQ{xcK7O=25|Q!i3E2H%A0rzV~VlIzsf}Ex4U!=NQUJ> zC;HysIGEWPk9@%p9!gRT2k9ZIlRiR>x9nk2qyF+m(*qFSIuT2p_ye9HsZ$Ic7EK>? zEEvF_Y}(?2(S5#3GZnMxCBY!D_4}@ap-o9dU7Ewu0`t%imZr)MngZ{q!u>mxuj#|G zppLL1Z7-J|X>lz}Em)_(3RflO^`r01?o^ozD1?@~W5HB(pg*`5YW4TiijBRC4|$e0 zedmAJxl!=hf+20ko&zqsaef6tygX6q{Ght6E>>uv210}XUhuz6N#uiJ%DESIo5Jr( z;zOgU-3MDB!TP?0N|Kl$Q`7`McVjQ%aW|cq?Oji!t`g0G8P(+ zMGr#XX=Dm<@x?ni@OhD^ZI~<_c9gj3x zCPuOfhUIPL<1fc94Q?4dckHQ6oCKN*f4`EbI&dk34ygPfqA!Ska|NRpzuHhNFR5vBAd(g@)g};KQH0N(p{gX8ERdgbl$! zO%u$LoEG$GmkW+F3;~;YK7Z_#buZ4^6{VTX{rc_e!5BJNq0Dchm%UzxSn}{gm zD~-_p?6y+8-3L2`s!~dYbE&=qy6JYU8rZvvwz)b7Pud}E;mx%!r)hqJE2g3>k3fps zDQ@)G=YFM~G$j>+N@=ftgJzXUMSP7(C3&Hlnh=7wf z->}r>R53{2Ifv1G&==B8uGEnR9gew_IK;`!%SCQM6#9NKC~>ATH=#`P^kF``pb;Vz z9mTu7VxN@Av0I7}u?~8iPD*sal@1e7infS_8O3={&p=3r-CDsdj@}hqTiI1|t_p(m z6d?1-fGSMow*|xW$cv`>4)c*|VoTH(4CVdtW*k2ZMIFCUqV=Do^S%Rj_uE+FR7cUp zDrmwotCm#Dt$ga{N&PQd7{%1+ML-3Ah`A6M1#(Nga}@$UXBs(O}1z$c~uPJqE{6 z@ZkbH#SuHHQ|WP(c6acW6nSwacW4YuMeI#@c`?0wzMdaSLk$H0FA?mH%$16NJ220I z4s47Si?sqNJAwrid#7THzK9 z>)inca(?WNfpcGR>6SE57tZC1Zj~RruHALbV zfY18oNsBhyVnTJ;kOc(;EKa0eeLC^P@i`GqzPfKcRky~!_MJSKnOXSSJ?639x!7|E z&PC7J45bwBxb{)FJe%fKwq@SlV)6LV!5A3Rt`jy4@^|XEbv&z7uBcfv8>(A#EZ9%H zo^l3PK_%=UZZ>3^Lkx#fj3EiAz*dt6V~*~SAKW$_Y-sD33d)+Y85JWR9(pnK%-_4Z zz)MlT1sm5oZKTsftJNUzsp{gxG_uPaK_>ahkw{saCm@<9u1m$n9z@`!Zi#`p!sDm* zO2w)sb7`8~F1J-KdVCkjW`l^lL2Z{Q1RwQf`PZYUC8d`$b%FDiNh zOATJP;@XjkJvY|^JMD;y+xu`g9@i5FhG{fE0JXA@eB1Qr`$7gdEugvNH1uUP;&s8M zP`>fU{t$v7L~db-zkklBROyeib_WMA4(SrQBYZ{%XkxkX@1h4)ar|iSAFEd%`LxyG-*8o*yjSL~Y8LlM z`A@6CbW8x+i>mF|DF6&H@^0DiSg8C@;UHK`Nki#jxg8hLF4Dait$ z&=1-DT(E7+=%q6YA0%{c=hOMFiG_l^x2Vca|9&;3J2B8GJtaRW`kRZ9C@A@h*aw|f zQaI}VHoIGQ+w%cTSo?aP@J%)f*(WYSs8d>jd1227JTZ-_0yklNFDWV@01K+ck zg2L{D+&#mdtxfMYT{OT^VKeG13h7};Xm+hd9=XQAx8}pKu?{I66nKnoEP4;mS4EMq zgsKo5m`K5Ax#p)oQSMl*;s38WZf_^GPxN2S@uLZhIsO2)Xmt6-*Zl1}b5p_1Rl!n> zF;2RX^6ldEpDSzt%X>Dc8lClIPfWmW_&zd8Hw$2I|>@>EbA$z;f-} zl|fyUaA8^XsD8-)9N8*G&8;w>0g>a+IV)Sn$Iz6#5xVuJiFHaoz5x!k`D-T$%ZR;^ zidV=W;9~jy-G(YI=5}zl0xCee}~{RVMXZ zxdnI3#w-xsKj`(xZfETI*KVKwWZlwvWN(C*yXqMTWWTFb`@$ve8joH|$gN7%Eba4} zY`qa@W93ztC+1KWkG97|4694~o-204@1U3OGk|l;ZSf0Iv-uLC!HN90LHixQTOj$_ z@oWoTsBf*$4m&1ADWkqdl3$57{gE8FePi!)lLrR<`rC_1e2EKc34t9n0qs{4-u>DM!y>8Ael z`sIpg&_`{0bJO3i0x(l^c9=Dpt?TD_UdLq78(1-@mF0a>Ro?N@P?$?}EW8qhtfKwc z{7}HCi)y&Ob%IF%EBQV>+A8C)$*l^K-gOzYS#R58eYCa{Bibzbb=xsm^NFn>BU+n5 z8nfI~v;@|=1g{pUKf8SzfUo#&B~1#h@84t2nxRW8d+I)TST_O5Z)+x`IBIB##buf0 z^5>0;MKCTo6Nu zMgWE4bJ~0Lz_nl8(C&xkBc|YrQDn0B12@h)Rm)^~Sy1i(Bh+k3xX3xiBfYC|rU;fv zg$W+ti=uTU_F1HsLsZ@KMumg;4ahgeZ`yr@|4?z ztzV&H1yS60&%_<&{<~Lzms;h#U%Dc~K46|@vU60RX^aBCqeZu5Fs^)@*!3DrexLdr zE90Rua`ljHk+Gr5ri8;W7%29xYA+prUIbTZdv9W#Peb$L8F=Y{dU+@nc=W~dsT|g^ zEkn}~=U()hq1N!tdKF@N(v2TOQRQWqB$+Doa~644MwiMkH!r*SwKtTze9aiiKh0ap z_#;W~x=;tr zy3zw8l`CnAJlL`_lF~om{kGJ+5EXQpE|?8g?erMW`bS0_VCSw%G8&4zE9k8uI~Z+2 zGj1>yJF5}`n$Y{~tO6D) zE`nbWo7hi8qZjur3tK@cGv%j(ip|nZ+9Vrg$|eg9#JNz5pL-#T3edtv1?MH%^2ff^ zE582BH%uQ&cZl^-V7>7@b}r^b4;7Xr2}wFFpA!sox*j)k<^{o_CD=b{Q4M}`fP@AN z@VWK00`=0kG_jfnuBw8O5@D0``qs{YX&JMVtd5hJvC|u2Q-?R0F907eA0PK)UT!`<4L)9B0Rds&C!D;z!o0k< kPHJzW;s05|+SUwi?)E=duqz;Lyy^TiStXegsn>r00UNQ#?*IS* diff --git a/sftp-connector-test/src_test/com/axonivy/connector/sftp/test/SftpProcessSSHTest.java b/sftp-connector-test/src_test/com/axonivy/connector/sftp/test/SftpProcessSSHTest.java new file mode 100644 index 0000000..698bda2 --- /dev/null +++ b/sftp-connector-test/src_test/com/axonivy/connector/sftp/test/SftpProcessSSHTest.java @@ -0,0 +1,144 @@ +package com.axonivy.connector.sftp.test; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.List; + +import org.apache.commons.io.FileUtils; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; + +import com.axonivy.connector.sftp.service.SftpClientService; +import com.axonivy.connector.sftp.service.SftpClientService.FileData; + +import ch.ivyteam.ivy.bpm.engine.client.BpmClient; +import ch.ivyteam.ivy.bpm.engine.client.element.BpmElement; +import ch.ivyteam.ivy.bpm.engine.client.element.BpmProcess; +import ch.ivyteam.ivy.bpm.engine.client.sub.SubProcessCallResult; +import ch.ivyteam.ivy.bpm.exec.client.IvyProcessTest; +import ch.ivyteam.ivy.environment.Ivy; +import ch.ivyteam.ivy.scripting.objects.File; + + +/** + * This SftpProcessTest simulates SFTP operations by calling the sub processes: + * SftpUploadFile and SftpDownloadFile. + * + *

The test can either be run

    + *
  • in the Designer IDE ( right click > run as > JUnit Test )
  • + *
  • or in a Maven continuous integration build pipeline ( mvn clean verify )
  • + *

+ * + *

Detailed guidance on writing these kind of tests can be found in our + * Process Testing docs + *

+ */ +@IvyProcessTest(enableWebServer = true) +public class SftpProcessSSHTest { + + private static final BpmProcess TEST_HELPER_PROCESS = BpmProcess.path("Sftp/SftpHelper"); + private static final BpmProcess TEST_UPLOAD_FILE_PROCESS = BpmProcess.path("Sftp/SftpUploadFile"); + private static final BpmProcess TEST_DOWNLOAD_FILE_PROCESS = BpmProcess.path("Sftp/SftpDownloadFile"); + + private static final String TEST_FILE_NAME = "market_market_connector_sftp.pdf"; + private static final long TEST_FILE_SIZE = 207569L; + + + @BeforeAll + public static void init() throws Exception { + String prefix = "com_axonivy_connector_sftp_server_"; + Ivy.var().set(prefix+"auth", "ssh"); + Ivy.var().set(prefix+"password", ""); + + String keyString = Files.readString(Paths.get(SftpProcessSSHTest.class.getResource("sftptest").toURI())); + Ivy.var().set(prefix+"secret_sshkey", keyString); + Ivy.var().set(prefix+"secret_sshpassphrase", "123456"); + } + + @Test + @Order(1) + public void callOpenConnection(BpmClient bpmClient) throws Exception { + BpmElement startable = TEST_HELPER_PROCESS.elementName("openConnection()"); + + SubProcessCallResult result = bpmClient.start() + .subProcess(startable) + .execute() // Callable sub process input arguments + .subResult(); + + SftpClientService sftpClient = result.param("sftpClient", SftpClientService.class); + assertThat(sftpClient).isNotNull(); + if (sftpClient != null) { + sftpClient.close(); + } + } + + @Test + @Order(2) + public void callUploadFile(BpmClient bpmClient) { + InputStream fileToBeUploaded = getClass().getResourceAsStream(TEST_FILE_NAME); + + BpmElement startable = TEST_UPLOAD_FILE_PROCESS.elementName("uploadFile(InputStream,String)"); + + SubProcessCallResult result = bpmClient.start() + .subProcess(startable) + .execute(fileToBeUploaded, TEST_FILE_NAME) // Callable sub process input arguments + .subResult(); + + Boolean isSuccess = result.param("isSuccess", Boolean.class); + assertThat(isSuccess).isTrue(); + } + + @Test + @Order(3) + public void callUploadIvyFile(BpmClient bpmClient) throws IOException { + InputStream fileToBeUploaded = getClass().getResourceAsStream(TEST_FILE_NAME); + java.io.File javaFile = new java.io.File(TEST_FILE_NAME); + FileUtils.copyInputStreamToFile(fileToBeUploaded, javaFile); + + File ivyFile = new File(TEST_FILE_NAME, true); + FileUtils.moveFile(javaFile, ivyFile.getJavaFile()); + + BpmElement startable = TEST_UPLOAD_FILE_PROCESS.elementName("uploadFile(File)"); + + SubProcessCallResult result = bpmClient.start() + .subProcess(startable) + .execute(ivyFile) // Callable sub process input arguments + .subResult(); + + Boolean isSuccess = result.param("isSuccess", Boolean.class); + assertThat(isSuccess).isTrue(); + } + + @Test + @Order(4) + public void callListAllFiles(BpmClient bpmClient) { + BpmElement startable = TEST_DOWNLOAD_FILE_PROCESS.elementName("listAllFiles(String)"); + + SubProcessCallResult result = bpmClient.start() + .subProcess(startable) + .execute(".") // Callable sub process input arguments + .subResult(); + List listFiles = result.param("listFiles", List.class); + assertThat(listFiles.size()).isGreaterThanOrEqualTo(1); + assertThat(listFiles).anyMatch(f -> f.getName().equals(TEST_FILE_NAME)); + } + + @Test + @Order(5) + public void callDownloadFile(BpmClient bpmClient) { + BpmElement startable = TEST_DOWNLOAD_FILE_PROCESS.elementName("downloadFile(String)"); + + SubProcessCallResult result = bpmClient.start() + .subProcess(startable) + .execute(TEST_FILE_NAME) // Callable sub process input arguments + .subResult(); + java.io.File downloadedFile = result.param("toFile", java.io.File.class); + assertThat(downloadedFile.length()).isEqualTo(TEST_FILE_SIZE); + assertThat(downloadedFile.getName()).isEqualTo(TEST_FILE_NAME); + } +} diff --git a/sftp-connector-test/src_test/com/axonivy/connector/sftp/test/SftpProcessTest.java b/sftp-connector-test/src_test/com/axonivy/connector/sftp/test/SftpProcessTest.java index e486466..fc93b9b 100644 --- a/sftp-connector-test/src_test/com/axonivy/connector/sftp/test/SftpProcessTest.java +++ b/sftp-connector-test/src_test/com/axonivy/connector/sftp/test/SftpProcessTest.java @@ -7,6 +7,7 @@ import java.util.List; import org.apache.commons.io.FileUtils; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; @@ -35,6 +36,7 @@ *

*/ @IvyProcessTest(enableWebServer = true) +@Disabled public class SftpProcessTest { private static final BpmProcess TEST_HELPER_PROCESS = BpmProcess.path("Sftp/SftpHelper"); @@ -124,5 +126,4 @@ public void callDownloadFile(BpmClient bpmClient) { assertThat(downloadedFile.length()).isEqualTo(TEST_FILE_SIZE); assertThat(downloadedFile.getName()).isEqualTo(TEST_FILE_NAME); } - } diff --git a/sftp-connector/config/variables.yaml b/sftp-connector/config/variables.yaml index 5a9221e..afe98cb 100644 --- a/sftp-connector/config/variables.yaml +++ b/sftp-connector/config/variables.yaml @@ -4,12 +4,21 @@ Variables: # The host name to the SFTP server host: 'localhost' - # The password to the SFTP server - # [password] - password: pwd - # The port number to the SFTP server port: 22 # The username to the SFTP server username: 'usr' + + # Auth type to the SFPT server: password OR ssh + auth: 'ssh' + + # The password to the SFTP server + # [password] + password: '' + + # The ssh key string to SFTP server + # [secret private key] + secret.sshkey: '' + # The ssh key passphrase + secret.sshpassphrase: '' diff --git a/sftp-connector/pom.xml b/sftp-connector/pom.xml index 119c50d..6d43de1 100644 --- a/sftp-connector/pom.xml +++ b/sftp-connector/pom.xml @@ -11,9 +11,9 @@ - com.jcraft + com.github.mwiede jsch - 0.1.55 + 0.2.19 diff --git a/sftp-connector/processes/Sftp/SftpHelper.p.json b/sftp-connector/processes/Sftp/SftpHelper.p.json index 20840f8..3e064b1 100644 --- a/sftp-connector/processes/Sftp/SftpHelper.p.json +++ b/sftp-connector/processes/Sftp/SftpHelper.p.json @@ -58,11 +58,13 @@ "}", "String username = ivy.var.variable(prefix+\"username\").value();", "String password = ivy.var.variable(prefix+\"password\").value();", + "String auth = ivy.var.get(prefix+\"auth\");", + "String ssh = ivy.var.get(prefix+\"secret_sshkey\");", + "String sshpassphrase = ivy.var.get(prefix+\"secret_sshpassphrase\");", "", "ivy.log.debug(\"The following settings will be used to connect to the SFTP server: hostname: {0}, port: {1}, username: {2}. Connection in progress...\", ", " host, port, username);", - "", - "in.sftpClient = new SftpClientService(host, port, username, password);", + "in.sftpClient = new SftpClientService(host, port, username, auth, password, ssh, sshpassphrase);", "", "ivy.log.debug(\"Connection established.\");" ] diff --git a/sftp-connector/src/com/axonivy/connector/sftp/service/SftpClientService.java b/sftp-connector/src/com/axonivy/connector/sftp/service/SftpClientService.java index 775b4b0..21c04a5 100644 --- a/sftp-connector/src/com/axonivy/connector/sftp/service/SftpClientService.java +++ b/sftp-connector/src/com/axonivy/connector/sftp/service/SftpClientService.java @@ -8,7 +8,9 @@ import java.util.ArrayList; import java.util.Date; import java.util.List; +import java.util.Properties; +import org.apache.commons.lang3.StringUtils; import org.apache.log4j.Logger; import com.jcraft.jsch.ChannelSftp; @@ -28,6 +30,7 @@ public class SftpClientService implements AutoCloseable { private static final String PATHSEPARATOR = "/"; private static final int SESSION_TIMEOUT = 10000; private static final int CHANNEL_TIMEOUT = 5000; + private static final String PASSWORD = "password"; /** @@ -44,17 +47,25 @@ public class SftpClientService implements AutoCloseable { * Instantiates the SftpClientService object with given the host, port, username and password. * * @param host the host name + * @param authType authentication type: password, ssh * @param port the port number * @param username the user name * @param password the password + * @param keyString the ssh key string + * @param passphrase the ssh passphrase * @throws IOException */ - public SftpClientService(String host, int port, String username, String password) throws IOException { + public SftpClientService(String host, int port, String username, String authType, String password, String keyString, String passphrase) throws IOException { try { JSch jsch = new JSch(); session = jsch.getSession(username, host, port); - session.setPassword(password); + if (StringUtils.isEmpty(authType) || PASSWORD.equalsIgnoreCase(authType)) { + session.setPassword(password); + } else { + session.setConfig("PreferredAuthentications", "publickey"); + jsch.addIdentity(null, keyString.getBytes(), null, passphrase.getBytes()); + } session.setConfig("StrictHostKeyChecking", "no"); // 10 seconds session timeout