From c70b44ca87b0e4bb2595713ea1ca6394a5dd9174 Mon Sep 17 00:00:00 2001 From: MaxKey Date: Sat, 7 Aug 2021 18:25:25 +0800 Subject: [PATCH] =?UTF-8?q?v=202.9.0=20&=20=E4=BC=81=E4=B8=9A=E5=BE=AE?= =?UTF-8?q?=E4=BF=A1&=E9=92=89=E9=92=89=E6=89=AB=E6=8F=8F=E7=99=BB?= =?UTF-8?q?=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit v 2.9.0 & 企业微信&钉钉扫描登录 --- ReleaseNotes.txt | 40 +++++++++++++++++- gradle.properties | 4 +- .../service/SocialSignOnProvider.java | 11 ++++- .../service/SocialSignOnProviderService.java | 15 ++++--- .../SocialSignOnAutoConfiguration.java | 7 +++ .../resources/application-http.properties | 9 ++++ .../resources/application-https.properties | 9 ++++ .../src/main/resources/application.properties | 2 +- .../src/main/resources/application.properties | 2 +- .../images/social/wechat_enterprise.png | Bin 1014 -> 1237 bytes 10 files changed, 87 insertions(+), 12 deletions(-) diff --git a/ReleaseNotes.txt b/ReleaseNotes.txt index 3cbbb6e98..4102f8281 100644 --- a/ReleaseNotes.txt +++ b/ReleaseNotes.txt @@ -1,4 +1,42 @@ -MaxKey v 2.8.1 GA 2021/06/25 +MaxKey v 2.9.0 GA 2021/08/** + *(MAXKEY-210601) 企业微信扫描登录 + *(MAXKEY-210602) 钉钉扫描登录 + *(MAXKEY-210603) 第三方登录异常问题修复 + *(MAXKEY-210604) 新建maxkey-webs目录,包含maxkey-web-maxkey、maxkey-web-mgt、maxkey-web-resources + *(MAXKEY-210605) 静态文件的合并到maxkey-web-resources + *(MAXKEY-210606) 腾讯企业邮箱适配器优化 + *(MAXKEY-210607) 移除*.xml的spring配置文件 + *(MAXKEY-210608) 后台管理员自动生成密码的问题修复 + *(MAXKEY-210609) 密码重置接口的优化 + *(MAXKEY-210610) KAFKA数据同步接口优化,重新定义同步TOPIC + *(MAXKEY-210611) LDAP及ActiveDirectory属性和连接的优化 + *(MAXKEY-210612) Synchronizers同步器的模块化,分成activedirectory、ldap、dingding、workweixin + *(MAXKEY-210613) Synchronizers增加定时同步功能 + *(MAXKEY-210614) 后台用户和机构查询排序优化 + *(MAXKEY-210615) 增加连接器日志查询 + *(MAXKEY-210616) 增加同步器日志查询 + *(MAXKEY-210617) 应用配置适配器不生效修复 + *(MAXKEY-210618) FormBased认证功能的优化 + *(MAXKEY-210619) FormBased密码首次初始化问题修复 + *(MAXKEY-210620) 重新登录地址优化 + *(MAXKEY-210621) 实体类型的ID全部调整为雪花ID + *(MAXKEY-210622) 后台报表优化 + *(MAXKEY-210623) 底层数据库mybatis-jpa-extra优化及问题修复 + *(MAXKEY-210624) 登录模块的模块化 + *(MAXKEY-210625) LDAP登录成功,密码自动同步到MaxKey + *(MAXKEY-210626) 社交账号企业微信LOGO + *(MAXKEY-210627) 找回密码时密码不匹配问题修复 + *(MAXKEY-210628) SHELL脚本优化 + *(MAXKEY-210629) 官方网站优化 + *(MAXKEY-210630) 依赖jar引用、更新和升级 + spring 5.3.9 + springBoot 2.5.3 + springSession 2.5.1 + tomcat 9.0.50 + JustAuth 1.16.2 + + +MaxKey v 2.8.1 GA 2021/06/25 *(MAXKEY-210501) 官方网站内容优化 *(MAXKEY-210502) 修复同步器LDAP保存保存问题 *(MAXKEY-210503) ReadMe内容优化 diff --git a/gradle.properties b/gradle.properties index a2f82df93..b8a971fde 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ #maxkey properties group =maxkey.top -version =2.8.2 +version =2.9.0 vendor =https://www.maxkey.top author =MaxKeyTop #maxkey used jars version @@ -87,7 +87,7 @@ jcipannotationsVersion =1.0 minidevjsonsmartVersion =2.3 minidevasmVersion =1.0.2 simplehttpVersion =1.0.3 -JustAuthVersion =1.16.1 +JustAuthVersion =1.16.2 javassistVersion =3.23.0-GA esapiVersion =2.2.0.0 javaxmailVersion =1.6.2 diff --git a/maxkey-authentications/maxkey-authentication-social/src/main/java/org/maxkey/authn/support/socialsignon/service/SocialSignOnProvider.java b/maxkey-authentications/maxkey-authentication-social/src/main/java/org/maxkey/authn/support/socialsignon/service/SocialSignOnProvider.java index 19e228c86..716194e4e 100644 --- a/maxkey-authentications/maxkey-authentication-social/src/main/java/org/maxkey/authn/support/socialsignon/service/SocialSignOnProvider.java +++ b/maxkey-authentications/maxkey-authentication-social/src/main/java/org/maxkey/authn/support/socialsignon/service/SocialSignOnProvider.java @@ -36,6 +36,7 @@ public class SocialSignOnProvider { private String lastLoginTime; private String state; private int sortOrder; + private boolean hidden; private boolean userBind; @@ -159,7 +160,15 @@ public class SocialSignOnProvider { this.state = state; } - @Override + public boolean isHidden() { + return hidden; + } + + public void setHidden(boolean hidden) { + this.hidden = hidden; + } + + @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append("SocialSignOnProvider [provider="); diff --git a/maxkey-authentications/maxkey-authentication-social/src/main/java/org/maxkey/authn/support/socialsignon/service/SocialSignOnProviderService.java b/maxkey-authentications/maxkey-authentication-social/src/main/java/org/maxkey/authn/support/socialsignon/service/SocialSignOnProviderService.java index 5a1128e68..7b0aeb2da 100644 --- a/maxkey-authentications/maxkey-authentication-social/src/main/java/org/maxkey/authn/support/socialsignon/service/SocialSignOnProviderService.java +++ b/maxkey-authentications/maxkey-authentication-social/src/main/java/org/maxkey/authn/support/socialsignon/service/SocialSignOnProviderService.java @@ -17,6 +17,7 @@ package org.maxkey.authn.support.socialsignon.service; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -34,7 +35,7 @@ import me.zhyd.oauth.request.*; public class SocialSignOnProviderService{ private static Logger _logger = LoggerFactory.getLogger(SocialSignOnProviderService.class); - List socialSignOnProviders; + List socialSignOnProviders = new ArrayList(); HashMapsocialSignOnProviderMaps=new HashMap(); @@ -135,14 +136,16 @@ public class SocialSignOnProviderService{ public void setSocialSignOnProviders( List socialSignOnProviders) { - - this.socialSignOnProviders = socialSignOnProviders; - + for(SocialSignOnProvider socialSignOnProvider : socialSignOnProviders){ - socialSignOnProviderMaps.put(socialSignOnProvider.getProvider(), socialSignOnProvider); + socialSignOnProviderMaps.put(socialSignOnProvider.getProvider(), socialSignOnProvider); + + if(!socialSignOnProvider.isHidden()) { + this.socialSignOnProviders.add(socialSignOnProvider); + } } - _logger.debug(""+socialSignOnProviders); + _logger.debug(""+this.socialSignOnProviders); } } diff --git a/maxkey-authentications/maxkey-authentication-social/src/main/java/org/maxkey/autoconfigure/SocialSignOnAutoConfiguration.java b/maxkey-authentications/maxkey-authentication-social/src/main/java/org/maxkey/autoconfigure/SocialSignOnAutoConfiguration.java index 8f43802bb..84c71bcf2 100644 --- a/maxkey-authentications/maxkey-authentication-social/src/main/java/org/maxkey/autoconfigure/SocialSignOnAutoConfiguration.java +++ b/maxkey-authentications/maxkey-authentication-social/src/main/java/org/maxkey/autoconfigure/SocialSignOnAutoConfiguration.java @@ -58,6 +58,7 @@ public class SocialSignOnAutoConfiguration implements InitializingBean { String clientSecret=applicationProperty.getProperty("maxkey.socialsignon."+provider+".client.secret"); String sortOrder = applicationProperty.getProperty("maxkey.socialsignon."+provider+".sortorder"); String agentId = applicationProperty.getProperty("maxkey.socialsignon."+provider+".agent.id"); + String hidden = applicationProperty.getProperty("maxkey.socialsignon."+provider+".hidden"); SocialSignOnProvider socialSignOnProvider = new SocialSignOnProvider(); socialSignOnProvider.setProvider(provider); @@ -68,6 +69,12 @@ public class SocialSignOnAutoConfiguration implements InitializingBean { socialSignOnProvider.setSortOrder(Integer.valueOf(sortOrder)); socialSignOnProvider.setAgentId(agentId); + if(hidden == null || hidden.equalsIgnoreCase("false")) { + socialSignOnProvider.setHidden(false); + }else if(hidden.equalsIgnoreCase("true")){ + socialSignOnProvider.setHidden(true); + } + _logger.debug("socialSignOnProvider " + socialSignOnProvider.getProvider() + "(" + socialSignOnProvider.getProviderName()+")"); _logger.trace("socialSignOnProvider " + socialSignOnProvider); diff --git a/maxkey-webs/maxkey-web-maxkey/src/main/resources/application-http.properties b/maxkey-webs/maxkey-web-maxkey/src/main/resources/application-http.properties index 0cbd24f00..1d97e4edc 100644 --- a/maxkey-webs/maxkey-web-maxkey/src/main/resources/application-http.properties +++ b/maxkey-webs/maxkey-web-maxkey/src/main/resources/application-http.properties @@ -360,6 +360,7 @@ maxkey.socialsignon.gitee.icon=images/social/gitee.png maxkey.socialsignon.gitee.client.id=ee6fdc484b3398d17e77d6ff37fd8b9fe502106398c7b22bf5522d3c01303f45 maxkey.socialsignon.gitee.client.secret=d6c3558f295f044df538c966a9084166f9a877c7a7392543184007a5faccdbad maxkey.socialsignon.gitee.account.id=id +maxkey.socialsignon.gitee.hidden=false maxkey.socialsignon.gitee.sortorder=1 #wechat maxkey.socialsignon.wechatopen.provider=wechatopen @@ -368,6 +369,7 @@ maxkey.socialsignon.wechatopen.icon=images/social/wechat.png maxkey.socialsignon.wechatopen.client.id=ee6fdc484b3398d17e7 maxkey.socialsignon.wechatopen.client.secret=7a5faccdbad maxkey.socialsignon.wechatopen.account.id=id +maxkey.socialsignon.wechatopen.hidden=false maxkey.socialsignon.wechatopen.sortorder=2 #work weixin maxkey.socialsignon.workweixin.provider=workweixin @@ -377,6 +379,7 @@ maxkey.socialsignon.workweixin.client.id=wx00d052e8f417f8f9 maxkey.socialsignon.workweixin.client.secret=lIy40iP0z4D65eJaWDNoe-vSlttmqY2WGJBygbM0TlY maxkey.socialsignon.workweixin.agent.id=1000002 maxkey.socialsignon.workweixin.account.id=id +maxkey.socialsignon.workweixin.hidden=true maxkey.socialsignon.workweixin.sortorder=2 #sina weibo maxkey.socialsignon.sinaweibo.provider=sinaweibo @@ -385,6 +388,7 @@ maxkey.socialsignon.sinaweibo.icon=images/social/weibo.png maxkey.socialsignon.sinaweibo.client.id=3379757634 maxkey.socialsignon.sinaweibo.client.secret=1adfdf9800299037bcab9d1c238664ba maxkey.socialsignon.sinaweibo.account.id=id +maxkey.socialsignon.sinaweibo.hidden=false maxkey.socialsignon.sinaweibo.sortorder=3 #Google maxkey.socialsignon.google.provider=google @@ -393,6 +397,7 @@ maxkey.socialsignon.google.icon=images/social/google.png maxkey.socialsignon.google.client.id=519914515488.apps.googleusercontent.com maxkey.socialsignon.google.client.secret=3aTW3Iw7e11QqMnHxciCaXTt maxkey.socialsignon.google.account.id=id +maxkey.socialsignon.google.hidden=false maxkey.socialsignon.google.sortorder=4 #dingtalk maxkey.socialsignon.dingtalk.provider=dingtalk @@ -401,6 +406,7 @@ maxkey.socialsignon.dingtalk.icon=images/social/dingtalk.png maxkey.socialsignon.dingtalk.client.id=dingoawf2jyiwh2uzqnphg maxkey.socialsignon.dingtalk.client.secret=Crm7YJbMKfRlvG2i1SHpg4GHVpqF_oXiEjhmRQyiSiuzNRWpbFh9i0UjDTfhOoN9 maxkey.socialsignon.dingtalk.account.id=openid +maxkey.socialsignon.dingtalk.hidden=false maxkey.socialsignon.dingtalk.sortorder=5 #QQ maxkey.socialsignon.qq.provider=qq @@ -409,6 +415,7 @@ maxkey.socialsignon.qq.icon=images/social/qq.png maxkey.socialsignon.qq.client.id=101225363 maxkey.socialsignon.qq.client.secret=8577d75e0eb4a91ac549cc8be3371bfd maxkey.socialsignon.qq.account.id=openid +maxkey.socialsignon.qq.hidden=false maxkey.socialsignon.qq.sortorder=6 #Microsoft maxkey.socialsignon.microsoft.provider=microsoft @@ -417,6 +424,7 @@ maxkey.socialsignon.microsoft.icon=images/social/microsoft.png maxkey.socialsignon.microsoft.client.id=24aa73b6-7928-4e64-bd64-d8682e650f95 maxkey.socialsignon.microsoft.client.secret=PF[_AthtjVrtWVO2mNy@CJxY1@Z8FNf5 maxkey.socialsignon.microsoft.account.id=id +maxkey.socialsignon.microsoft.hidden=false maxkey.socialsignon.microsoft.sortorder=7 #facebook maxkey.socialsignon.facebook.provider=facebook @@ -425,6 +433,7 @@ maxkey.socialsignon.facebook.icon=images/social/facebook.png maxkey.socialsignon.facebook.client.id=appKey maxkey.socialsignon.facebook.client.secret=appSecret maxkey.socialsignon.facebook.account.id=id +maxkey.socialsignon.facebook.hidden=false maxkey.socialsignon.facebook.sortorder=8 ############################################################################ diff --git a/maxkey-webs/maxkey-web-maxkey/src/main/resources/application-https.properties b/maxkey-webs/maxkey-web-maxkey/src/main/resources/application-https.properties index 90a934d86..55bf0c6cc 100644 --- a/maxkey-webs/maxkey-web-maxkey/src/main/resources/application-https.properties +++ b/maxkey-webs/maxkey-web-maxkey/src/main/resources/application-https.properties @@ -353,6 +353,7 @@ maxkey.socialsignon.gitee.icon=images/social/gitee.png maxkey.socialsignon.gitee.client.id=ee6fdc484b3398d17e77d6ff37fd8b9fe502106398c7b22bf5522d3c01303f45 maxkey.socialsignon.gitee.client.secret=d6c3558f295f044df538c966a9084166f9a877c7a7392543184007a5faccdbad maxkey.socialsignon.gitee.account.id=id +maxkey.socialsignon.gitee.hidden=false maxkey.socialsignon.gitee.sortorder=1 #wechat maxkey.socialsignon.wechatopen.provider=wechatopen @@ -361,6 +362,7 @@ maxkey.socialsignon.wechatopen.icon=images/social/wechat.png maxkey.socialsignon.wechatopen.client.id=ee6fdc484b3398d17e7 maxkey.socialsignon.wechatopen.client.secret=7a5faccdbad maxkey.socialsignon.wechatopen.account.id=id +maxkey.socialsignon.wechatopen.hidden=false maxkey.socialsignon.wechatopen.sortorder=2 #work weixin maxkey.socialsignon.workweixin.provider=workweixin @@ -370,6 +372,7 @@ maxkey.socialsignon.workweixin.client.id=ww61ca142e1fe5b8ca maxkey.socialsignon.workweixin.client.secret=CfWEoALuKdSKzXiV-QWXeGSD5zPd6Svze3GR_gB5eFs maxkey.socialsignon.workweixin.agent.id=1000002 maxkey.socialsignon.workweixin.account.id=id +maxkey.socialsignon.workweixin.hidden=true maxkey.socialsignon.workweixin.sortorder=2 #sina weibo maxkey.socialsignon.sinaweibo.provider=sinaweibo @@ -378,6 +381,7 @@ maxkey.socialsignon.sinaweibo.icon=images/social/weibo.png maxkey.socialsignon.sinaweibo.client.id=3379757634 maxkey.socialsignon.sinaweibo.client.secret=1adfdf9800299037bcab9d1c238664ba maxkey.socialsignon.sinaweibo.account.id=id +maxkey.socialsignon.sinaweibo.hidden=false maxkey.socialsignon.sinaweibo.sortorder=3 #Google maxkey.socialsignon.google.provider=google @@ -386,6 +390,7 @@ maxkey.socialsignon.google.icon=images/social/google.png maxkey.socialsignon.google.client.id=519914515488.apps.googleusercontent.com maxkey.socialsignon.google.client.secret=3aTW3Iw7e11QqMnHxciCaXTt maxkey.socialsignon.google.account.id=id +maxkey.socialsignon.google.hidden=false maxkey.socialsignon.google.sortorder=4 #dingtalk maxkey.socialsignon.dingtalk.provider=dingtalk @@ -394,6 +399,7 @@ maxkey.socialsignon.dingtalk.icon=images/social/dingtalk.png maxkey.socialsignon.dingtalk.client.id=dingoawf2jyiwh2uzqnphg maxkey.socialsignon.dingtalk.client.secret=Crm7YJbMKfRlvG2i1SHpg4GHVpqF_oXiEjhmRQyiSiuzNRWpbFh9i0UjDTfhOoN9 maxkey.socialsignon.dingtalk.account.id=openid +maxkey.socialsignon.dingtalk.hidden=false maxkey.socialsignon.dingtalk.sortorder=5 #QQ maxkey.socialsignon.qq.provider=qq @@ -402,6 +408,7 @@ maxkey.socialsignon.qq.icon=images/social/qq.png maxkey.socialsignon.qq.client.id=101225363 maxkey.socialsignon.qq.client.secret=8577d75e0eb4a91ac549cc8be3371bfd maxkey.socialsignon.qq.account.id=openid +maxkey.socialsignon.qq.hidden=false maxkey.socialsignon.qq.sortorder=6 #Microsoft maxkey.socialsignon.microsoft.provider=microsoft @@ -410,6 +417,7 @@ maxkey.socialsignon.microsoft.icon=images/social/microsoft.png maxkey.socialsignon.microsoft.client.id=24aa73b6-7928-4e64-bd64-d8682e650f95 maxkey.socialsignon.microsoft.client.secret=PF[_AthtjVrtWVO2mNy@CJxY1@Z8FNf5 maxkey.socialsignon.microsoft.account.id=id +maxkey.socialsignon.microsoft.hidden=false maxkey.socialsignon.microsoft.sortorder=7 #facebook maxkey.socialsignon.facebook.provider=facebook @@ -418,6 +426,7 @@ maxkey.socialsignon.facebook.icon=images/social/facebook.png maxkey.socialsignon.facebook.client.id=appKey maxkey.socialsignon.facebook.client.secret=appSecret maxkey.socialsignon.facebook.account.id=id +maxkey.socialsignon.facebook.hidden=false maxkey.socialsignon.facebook.sortorder=8 ############################################################################ diff --git a/maxkey-webs/maxkey-web-maxkey/src/main/resources/application.properties b/maxkey-webs/maxkey-web-maxkey/src/main/resources/application.properties index a9af8bbea..98d62eb55 100644 --- a/maxkey-webs/maxkey-web-maxkey/src/main/resources/application.properties +++ b/maxkey-webs/maxkey-web-maxkey/src/main/resources/application.properties @@ -17,7 +17,7 @@ ############################################################################ application.title=MaxKey application.name=MaxKey -application.formatted-version=v2.8.2 GA +application.formatted-version=v2.9.0 GA ############################################################################ #spring.profiles.active https/http; default https # diff --git a/maxkey-webs/maxkey-web-mgt/src/main/resources/application.properties b/maxkey-webs/maxkey-web-mgt/src/main/resources/application.properties index 9da6a96f1..623573f58 100644 --- a/maxkey-webs/maxkey-web-mgt/src/main/resources/application.properties +++ b/maxkey-webs/maxkey-web-mgt/src/main/resources/application.properties @@ -17,7 +17,7 @@ ############################################################################ application.title=MaxKey application.name=MaxKey-Mgt -application.formatted-version=v2.8.2 GA +application.formatted-version=v2.9.0 GA ############################################################################ #spring.profiles.active http; default http # diff --git a/maxkey-webs/maxkey-web-resources/src/main/resources/static/images/social/wechat_enterprise.png b/maxkey-webs/maxkey-web-resources/src/main/resources/static/images/social/wechat_enterprise.png index 7cc8c739d7f8dea8a9584e44fb69d0f2120aaef4..fc7b3131e34adcd8ad7eab7fe901eb9fa48faf5b 100644 GIT binary patch literal 1237 zcmYjP3s6*L6h6zYAR8!V>PTWRLB4o67`4G*VT-#P2((c6NFFLInDUAk1fpdhyu@0R ztd_7yP#{(kkih~$UZt#zu&K!mr4j?E?1P62yYB7$cN?ZV|NZZ|=X~FH9{*ny7Q(Y< zJF)@5Ucl!H@#{?9rA*wpRmFYyVTl5E1pr+9@&!}u68vX;BINCW3j^yW0jySr1xEz( zQi4r!K&b(~r9i5OlN3nw#IUR{=wI~6rin+UCoqbF4jNo%0UIzxr2t50K!&z~2J2`Y z9brjyAfF{%vN+HH4TtI19GJ6pn!;Yw1FueplA*akkNUGzNIgc$fF&oBR~y+XFCw?R}{&G z^!#Z`L`LM<2JTmZ(B(wU7)Owv9pK#uUhRN%bdsHRLLL#syf`iD2BDLM6_S?qaD0a9 z2b??sSVC&LJgE+ySSOW0<1b-HIw}?rjNL8jJWn8gGAmq#PeFoR(`RRyl4ZxUu;juJ zITw0{=)tBI2WwFvQaWr0>&If6RU&`9E2?y1NH~~H?BQk4&B_Mb@kgO9;Y%1SlXcN& z57%x=de3*-SoL^rO_pjoGx%4ZJVp6mZWDI|VFQ5R}26>mMB#_`8TdkN&6#g!@=lO)5dD!X1mO1ehGh@c79xJG~GV$nrSxwqupK7FvG|;CMGp^ ztQL%38O@s-8B~t%+nC(z)HrXRHg2Qt9~(Vi+GR!<@){lO5y6anSVNMDXU;B zdb{2f@9WLp)aBxBYhtyIw_dgqyGo)MQJi(t$K`IUcINK1j%DTZHl6kb%E;3rp$dDk zqPw#6$AzU?z^M?X*HqPLL#%7dQ}QPcSJwnsok+d5A}uYD>6oOQN<2_l$IewcBpy4G zl&p;M8@?Og-4LttD&>{+Jn*oxd3H%YySZmd+$HsH9{TB_`WCPEL50uB^{+UeR=v(C zn`#lrnC=Sj`NpVu=a*ME+-w`tE^HrF*g1^xn2|e&W_>!NnZ9c^Q@y)nds%~}t}i*c zxOdu4o*Vn{)b_n&hWNe$;gy+;ZTJ1M`lEV^`L1BptL?zMA?a~51NWdL>jPAQ^f~q zM0aO4!3%0oF-mLP%*-bnlg;dAcRQO@`qFuuneTk(H|L!He}DeO8NPUi^W!W)ifWcz4!2Ir3}Y z=wGXWX$a6W)!!NO5(f#~8Oc=mEnx6e$s9QtNgooe07yC6Yax70;DUC|0_dfZX+P0U zTn#|d9k3V}$A1WJteQv@A}_3Ofq*$ce-P{pCzLeoTK0jc5X%7SkENZ&R{*vx1&i44 zrgM*On(5zg-fVn^;JRgF-RGG0+bJh|KZQ4|G0@+B+)GaP=6{X?Q3XgAhVC+`L*bwR zc(r8ac6NU^xVhsWqex)=()&QlmMq&&Ihk82eo|M37k^#Lex@yeR59~9!H4RD1RVv~ zMPN%!5@3$d#D5jzCI}}$90{OF2**p7{YF~==|W}<lkysIrp7qcG$xIHR>AU{AbY{;+(>;l=*ELPAg#67N+d!=4e z1xOV#`+rG35*5HR@Oa$EAc*ZzV*tJ8nD%2~eW(J1cLT=zQOpD!1~5rrXG^_Q25}VP z4ImN~Z2&!6GVK?Z;zG+1OgY)YiY>Q61}`2w8260jA(P5;@{)-}qATX3GMI~(_Bp1# zJ6+7|2Do?Oq2zf9nbW-^)0Nx0jc8%-{~X{sO@F^k`ca@w;>nQaPcJ@yY0OV_QTQSR zFb?Q>i2FcZSH%MMx@PX_6#$h7y@kvUgUHwHhP5!93C0>2|2@wq@1A}DIHnt**73J0Jvl^;b#)2Ner$QAiNqH zR(~K}%)Bh{W*OL1O65MRBQN=M-X$DF?h%=>EwSXeQofvXMQI7G1f z)chKC(m&k`j(cHk -- GitLab