From 5d0a1010c4d95bb0655e5ae5b1623afa72af548e Mon Sep 17 00:00:00 2001 From: LordYi Date: Fri, 2 Feb 2024 17:26:53 +0800 Subject: [PATCH] feat: using jcasbin in springsecurity --- .github/semantic.yml | 2 + .github/workflows/gradle-ci.yml | 85 ++++++++++++++++ .github/workflows/maven-ci.yml | 73 +++++++++++++ .gitignore | 3 + README.md | 20 +++- examples/rbac_model.conf | 14 +++ examples/rbac_policy.csv | 5 + examples/rbac_with_domains_model.conf | 14 +++ examples/rbac_with_domains_policy.csv | 0 img.png | 0 img_1.png | Bin 0 -> 29479 bytes img_2.png | 0 img_3.png | 0 pom.xml | 45 ++++++++ src/main/java/org/casbin/Application.java | 26 +++++ .../java/org/casbin/api/CommonResult.java | 96 ++++++++++++++++++ src/main/java/org/casbin/api/IErrorCode.java | 20 ++++ src/main/java/org/casbin/api/ResultCode.java | 40 ++++++++ .../org/casbin/config/UserSecurityConfig.java | 31 ++++++ .../casbin/controller/AdapterController.java | 85 ++++++++++++++++ src/main/resources/application.yml | 12 +++ src/test/java/org/casbin/JdbcTest.java | 56 ++++++++++ src/test/resources/application.yml | 7 ++ 23 files changed, 633 insertions(+), 1 deletion(-) create mode 100644 .github/semantic.yml create mode 100644 .github/workflows/gradle-ci.yml create mode 100644 .github/workflows/maven-ci.yml create mode 100644 examples/rbac_model.conf create mode 100644 examples/rbac_policy.csv create mode 100644 examples/rbac_with_domains_model.conf create mode 100644 examples/rbac_with_domains_policy.csv create mode 100644 img.png create mode 100644 img_1.png create mode 100644 img_2.png create mode 100644 img_3.png create mode 100644 pom.xml create mode 100644 src/main/java/org/casbin/Application.java create mode 100644 src/main/java/org/casbin/api/CommonResult.java create mode 100644 src/main/java/org/casbin/api/IErrorCode.java create mode 100644 src/main/java/org/casbin/api/ResultCode.java create mode 100644 src/main/java/org/casbin/config/UserSecurityConfig.java create mode 100644 src/main/java/org/casbin/controller/AdapterController.java create mode 100644 src/main/resources/application.yml create mode 100644 src/test/java/org/casbin/JdbcTest.java create mode 100644 src/test/resources/application.yml diff --git a/.github/semantic.yml b/.github/semantic.yml new file mode 100644 index 0000000..b172b6a --- /dev/null +++ b/.github/semantic.yml @@ -0,0 +1,2 @@ +# Always validate the PR title AND all the commits +titleAndCommits: false diff --git a/.github/workflows/gradle-ci.yml b/.github/workflows/gradle-ci.yml new file mode 100644 index 0000000..bfba7ab --- /dev/null +++ b/.github/workflows/gradle-ci.yml @@ -0,0 +1,85 @@ +name: build + +on: [push, pull_request] + +jobs: + build: + runs-on: ubuntu-latest + services: + mysql: + image: mysql + env: + MYSQL_ROOT_PASSWORD: casbin_test + MYSQL_DATABASE: casbin + MYSQL_USER: casbin_test + MYSQL_PASSWORD: TEST_casbin + ports: + - 3306:3306 + options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 + postgres: + image: postgres + env: + POSTGRES_DB: casbin + POSTGRES_USER: casbin_test + POSTGRES_PASSWORD: TEST_casbin + ports: + - 5432:5432 + options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 + sqlserver: + image: mcr.microsoft.com/mssql/server:2019-latest + env: + SA_PASSWORD: 9G3iqmzQDw9zCXII + ACCEPT_EULA: Y + ports: + - 1433:1433 + + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + fetch-depth: '0' + + - name: Install mssql-tools + run: | + curl https://packages.microsoft.com/keys/microsoft.asc | sudo apt-key add - + curl https://packages.microsoft.com/config/ubuntu/16.04/prod.list | sudo tee /etc/apt/sources.list.d/msprod.list + sudo apt-get update + sudo apt-get install mssql-tools unixodbc-dev + echo 'export PATH="$PATH:/opt/mssql-tools/bin"' >> ~/.bash_profile + + - name: Create database for sqlserver + run: sqlcmd -S 127.0.0.1,1433 -U sa -P '9G3iqmzQDw9zCXII' -Q "CREATE DATABASE casbin" + + - name: Set up JDK 1.8 + uses: actions/setup-java@v1 + with: + java-version: 1.8 + server-id: ossrh + server-username: OSSRH_JIRA_USERNAME + server-password: OSSRH_JIRA_PASSWORD + gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }} + gpg-passphrase: GPG_PASSPHRASE + + - name: Build with Maven + run: mvn clean test cobertura:cobertura + + - name: Codecov + uses: codecov/codecov-action@v1 + with: + token: ${{ secrets.CODECOV_TOKEN }} + + - name: Set up Node.js + uses: actions/setup-node@v2 + with: + node-version: 18 + + - name: Sematic Release + run: | + npm install -g @conveyal/maven-semantic-release semantic-release + semantic-release --prepare @conveyal/maven-semantic-release --publish @semantic-release/github,@conveyal/maven-semantic-release --verify-conditions @semantic-release/github,@conveyal/maven-semantic-release --verify-release @conveyal/maven-semantic-release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GPG_KEY_NAME: ${{ secrets.GPG_KEY_NAME }} + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} + OSSRH_JIRA_USERNAME: ${{ secrets.OSSRH_JIRA_USERNAME }} + OSSRH_JIRA_PASSWORD: ${{ secrets.OSSRH_JIRA_PASSWORD }} diff --git a/.github/workflows/maven-ci.yml b/.github/workflows/maven-ci.yml new file mode 100644 index 0000000..5bca5bd --- /dev/null +++ b/.github/workflows/maven-ci.yml @@ -0,0 +1,73 @@ +name: build + +on: [push, pull_request] + +jobs: + build: + runs-on: ubuntu-latest + services: + mysql: + image: mysql + env: + MYSQL_ROOT_PASSWORD: casbin_test + MYSQL_DATABASE: casbin + MYSQL_USER: casbin_test + MYSQL_PASSWORD: TEST_casbin + ports: + - 3306:3306 + options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 + postgres: + image: postgres + env: + POSTGRES_DB: casbin + POSTGRES_USER: casbin_test + POSTGRES_PASSWORD: TEST_casbin + ports: + - 5432:5432 + options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 + sqlserver: + image: mcr.microsoft.com/mssql/server:2019-latest + env: + SA_PASSWORD: 9G3iqmzQDw9zCXII + ACCEPT_EULA: Y + ports: + - 1433:1433 + + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + fetch-depth: '0' + + - name: Install mssql-tools + run: | + curl https://packages.microsoft.com/keys/microsoft.asc | sudo apt-key add - + curl https://packages.microsoft.com/config/ubuntu/16.04/prod.list | sudo tee /etc/apt/sources.list.d/msprod.list + sudo apt-get update + sudo apt-get install mssql-tools unixodbc-dev + echo 'export PATH="$PATH:/opt/mssql-tools/bin"' >> ~/.bash_profile + - name: Create database for sqlserver + run: sqlcmd -S 127.0.0.1,1433 -U sa -P '9G3iqmzQDw9zCXII' -Q "CREATE DATABASE casbin" + + - name: Set up JDK 1.8 + uses: actions/setup-java@v1 + with: + java-version: 1.8 + server-id: ossrh + server-username: OSSRH_JIRA_USERNAME + server-password: OSSRH_JIRA_PASSWORD + gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }} + gpg-passphrase: GPG_PASSPHRASE + + - name: Build with Maven + run: mvn clean test cobertura:cobertura + + - name: Codecov + uses: codecov/codecov-action@v1 + with: + token: ${{ secrets.CODECOV_TOKEN }} + + - name: Set up Node.js + uses: actions/setup-node@v2 + with: + node-version: 20.8.1 \ No newline at end of file diff --git a/.gitignore b/.gitignore index a1c2a23..489ed35 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,6 @@ # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* + +.idea/ +*.iml diff --git a/README.md b/README.md index a0125fc..5172ed5 100644 --- a/README.md +++ b/README.md @@ -1 +1,19 @@ -# casbin-spring-security-starter \ No newline at end of file +# using casbin in SpringSecurity + +## inform + To skip the tedious and complicated login process of Spring Security, + we're using its built-in login mode, which allows us to focus on the model authentication module. + +## DataBase + For the database, we're using mysql. + Remember to create a casbin database if you don't have one, and configure it in the application.yml file. + +## Simple Example + check file examples.rbac.policy.csv you can find out we are using rbac model + I registered with the identity of Alice who can read data1 +![img_1.png](img_1.png) + + Than test the result +![img_2.png](img_2.png) + +![img_3.png](img_3.png) \ No newline at end of file diff --git a/examples/rbac_model.conf b/examples/rbac_model.conf new file mode 100644 index 0000000..71159e3 --- /dev/null +++ b/examples/rbac_model.conf @@ -0,0 +1,14 @@ +[request_definition] +r = sub, obj, act + +[policy_definition] +p = sub, obj, act + +[role_definition] +g = _, _ + +[policy_effect] +e = some(where (p.eft == allow)) + +[matchers] +m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act \ No newline at end of file diff --git a/examples/rbac_policy.csv b/examples/rbac_policy.csv new file mode 100644 index 0000000..f93d6df --- /dev/null +++ b/examples/rbac_policy.csv @@ -0,0 +1,5 @@ +p, alice, data1, read +p, bob, data2, write +p, data2_admin, data2, read +p, data2_admin, data2, write +g, alice, data2_admin \ No newline at end of file diff --git a/examples/rbac_with_domains_model.conf b/examples/rbac_with_domains_model.conf new file mode 100644 index 0000000..57c3721 --- /dev/null +++ b/examples/rbac_with_domains_model.conf @@ -0,0 +1,14 @@ +[request_definition] +r = sub, dom, obj, act + +[policy_definition] +p = sub, dom, obj, act + +[role_definition] +g = _, _, _ + +[policy_effect] +e = some(where (p.eft == allow)) + +[matchers] +m = g(r.sub, p.sub, r.dom) && r.dom == p.dom && r.obj == p.obj && r.act == p.act \ No newline at end of file diff --git a/examples/rbac_with_domains_policy.csv b/examples/rbac_with_domains_policy.csv new file mode 100644 index 0000000..e69de29 diff --git a/img.png b/img.png new file mode 100644 index 0000000..e69de29 diff --git a/img_1.png b/img_1.png new file mode 100644 index 0000000000000000000000000000000000000000..448d6b55a89bf413c905d654b413ae80dc065713 GIT binary patch literal 29479 zcmeFZWmKC{w=POuprw=o#ak$Dg%pR-7He^L2*ry9DH;Mb+=_cr+$Fe%3L2~w3trq3 zC;<{6$VvO%{hhtXELJ`>ryCp1RU}V?3WzM1jt!FrdT`uS<3>GY#;I* zJnG-e1RUkwA<1>|SM2^D`IrTRwD#v44U+Rwjq;NP=wIYBYdRk9c>?h^p#9 z8;5ra5FGS(nyA1Fq^CQO*Pg>0tTFqa|9-5y25C)BHKN|e5KH$$o zQcxl23uW>h#Ll}PYuKG<9Y}Uf4p`&q!(NgOqXRDxc7@JyLZ)YPerwFokEajc+_9eb z8DgGoK0Z8Y%00g3x-F1R+%#-Y=YDE$UkEIqgU+3KGZ2AthtD5Y9yg@G4u|DZY;mR6 zL;3ba$jDTlx4$Lcnbd$R1)Mho5gUkrqu|?S80xg07T(|C=R;X1Gp5hE*s?5^^S^xE z_aj1{B8Gg&fKYYTAaOmoGgvO|fLt!l$S~*BCf_1+2;n*F(tYrpV`^@8Vq%tMtZ(l( zgNw5tdzJ>6KUR_)a5ZIoWMj83?(uQ#e&VV z8Xr3&wRaE|LSR=H7eUYjY4ZCn{v72d8zX!pF+V9pQB$oJnW5cLWMo9rmJnWX$=HJ{ zeRr5ReQ-{Yz3*?1mk0&dV19O%ClJ<|`C}VD1=fP)=hwLL;a>>t$h$|~?E(7^=#vrp z3pGs*9|s3LD}d(LT&iYBdYOKWkv@uLWdySX?PX0BR)(JkE#gDt=)hqqzPjh)JJ7Rp zMmn&du? ze@Xk}PF^;Bh=IxI8oC5IMlBZNmRI`b1U_0vZe`#;Pn&^EpD=7#ec0$HNkMY+TSVr}w_mC3ctv6Xoh`yz%sO(h^5_}F zO_DX1-c|@MylCY3TD0ad+1qO`Ne83K8ssW@wFTjEeulcWRaw6)9`w7JiU>}Nta zo+s;!gMHhbKThWZRp#Mbq|_ti{VnQ+nHkvWA!7Wd=V`GPM`N-(Alh% zX{+v71Ld4G>6WROQfHr5f$V0d*ak13?r9BrT@Bwl@+saNx>MkUl|J#r#NUap?V;6} z(v+i3WPw5Xb4fRUTQyd7ono*H&|CGa#lP=`jrN#9Vb|;naKVTjMmIn0M#Vrw6Eob9 zCj8vc1p3+^JB`TQ*#QRBsNXu=rHq=Hzz@nUodT>HiG(=_G~hUk68KUYXYKkBj^$U#< zb@fLbp56P;BWtMZz^|>HoS?$6qo?)Lw@~7YeIG=6Il2q3jYWH#%sc7jx78gL9S!zF zdE?53di3Zbgepp(jbi;jjEp$?KlGG=7+J5Ty&^XPdU7i9kXcf$)48!q;xcsyzfN@p z{a)ZAtW2B|2_ov=yQKzqYNsw?%Tivx9bwG_K)(&O5_-o&!RbNEw777na@i+)E;;Ow z^Kr;tH6uc^FTjuJ?Lx8+p59B2GS~Xl;SKF~JAg~>(bTGZfe1|sc8kTKlFE~Or)8$H zs8w1ZLv)>ajL(g>wpB7wuIWxzb3;LxtXKbaH#WQ%thaVpatXEv^3-ZyZPlvimV9Ji zCNe$1zj)fE1Hb;NBw8I>-Bh2b11;S~+uO7aj9q!HUq94)x#(76Q-hFHV^uZsb^o(! zYdFN{!0>FvGx|Ci`Vz^`bsJR3q-gh8x)>^31ntb73gq@yxST-vSOd#g_V3VLtHD56 zU7;s)`f0~MSpzjgo>N^0Ox%(DB|8tfZvOlXu@lC=xiS!-jOWI?Nbd#Xu#*?}%-((2 z*uxvXN87jdPWPH-Ht(fzN~*5(;C7YBoB7B*khmIK@o+27We2cG=YR}f+d zqG2X_$P7M|v`)elsaFmXY|*hlx)wi-?5UP|Pn7TtE7t6#qM#*SgBmuQag=Gl^$K^p zaOGc-zWfPS^tmbzo`mrlqyf%xR75h%$_>&!dc*ckF0@n1ooF%gt4@!-tCU@8wu@H)-Dge-1^rNV@680n&Jt^*e<(RMik=u_)GGvDm9C~kfVt9y62U{X zscScx)JS`H%0T305KI@}4}QL|-f5Ax##HW0Wp-%t3z));tqb}Hn z|DI~eJHxKpVZBXVEF@wW&qef(UYMZkK0Yo;5np~LKn=U;TgoGB1wuJ9P#CNGG4_o> zp(ML2a$k^x+yqGS(&oA(i$lk`FNHz zT1*0K$rCgBU<2AXdX#r|> zReQYi-%o(4l-`F z4%By^;pi-MK4FRWf!&fC{%g8I&kSDA#M zCo;U>l8%GezvuSU^jFqubx%uK+X1W=tlTH-4PLx@WrdxTYhZHO@zxyF01xBwGJBI> zbI8cN-;rE?oHTmnZ1g4mAkgK@>64xCLx8d3Tn9S%mcxxCdlKrYBsNaYHnjEAp&IP5 z=Mtv&_V&4KbN4wktR6$vgpONJu}|>yLu;t{wLTSJ7)cgrX-@Ozx)D1Ip{(O_+|0u6 zgk-e&G3JzH2*}WYgM))((UZ@2N@;nuF{KrP>)}%ut>K6PR_MO=>4%?wB>25QfN+Y! znmzc7f(t4tG}@zB?^%ugf!3jvtmERDDWoUb1{86S!-Y^vW~n?%#3ifulp=dsRrQ|l z{sFV89QOMTpS`jXmHq0X`mVR^3)<&Pz2Cbu^gpOYc%Qeq`Ruz5EDax9OLV9i(a^^6X3YWEuf8-^*`0kHJ?p*&*sS4Tk%YS5j`ryvL@W@AO2Qoc9-E(cw z%gc*{G?j{iLS%AsGP`ojdhaC;oi~2~wkqgYM}p~WW(`;v6tYO45f^=f>}?}M#M_5f zO54i>uHp6*X9DHfTETk)?1x}ustEHu zSu(Qf@T!HBle6XULCj~}n9A8mXjdQLh=z_hm?cg4Ay2uHPm(2{{hN}iYNSO;K)pSW z^kU+YQ-k%qeao4~ymx08vZ6w;?K&CR=TTSy?q%DHqouVo(SIAnD(9p5caQ>GHXVuB zC8mRxdj`dAkEgI`E{o&(KZoQkv`5IpCNx6?`d7z~+I!-dT~YgrB-Tgv>4Z($yOC#v z4k2Wukty-_9i9Ii{XUqq9dB9x`2acXAH@g$yJ!Av|KV;V)8s;d8mI$l`H(cJSHJ4?E#Y?aY zKR(@-XK~6}5OWclZkvdjB!D)XAvIfrZ|WoOc9)(eSx*Mz4g&4#An&hf3M0UQE>12MLi|$)D+2>7TYpvvi<(BONVOq- z+hoUKW=5)!mY+pT^b|PtXJD;$)lGGX#Ke5;o>TQ3ewNr+fr>SHBdTmiD55bnch6!Q z{cHaXr?-Fgs(@HV_htTJ0EQAzR2)Q$0DEBF(MwyNql+6yAv_f-vlRD{o&(AcN# z`@1&{!D`J4P5`_5UuZAA&D#63!<;*OM@#dOHLI1|=lHtw-95f@_ESoNEw07K9sucC z1a$!8j=HY3$oWgU6FTcVom?8goIw932!ewjxQ>aABs5$b60!hT*mtqsfHti}`3LRd z-z?^C@a>eu&N&p17B&;u;=(MJb&bMYx{&B!C%Db2sVP*>(i9(C_hJP18!;}$Yr3l2 zB8C{#`(|2jLCLA9)`Bb^#sv3#aG)*J)5*yb>f++AcEZe>0XJlQlpa#rY69g_O2Q#kt6AYBoWZyEVLWkkv-Ek>%Z_t!Uc*nuMQTZ7S*zXwn{H7kqRzeM z+G7RVnx$_io5^i2M;lop^U^KLMNN9Bi8A%2{Iy&>YnHA>GNu${DqsFyl7LFM5Gs>E zB%F2e9=RUxhzAlhOV1|`YjDvZfxwd%&7eIZ-1TI&Zs$By+jk}C91j-@Bu@DR?Pdif zu+9=2_Q$}})U9&0$J3DZlkbI$?Pqw#78W9J`^GQ_);RU_XTxj-$?~C_T7cg0zJD6+Kox*8?X$VZE_qoaCIXj_fe3D3=jl#_aDV$Nbj)|^oq zD`TdG*>o*K%p&QO>AvxIa}M+xQRBq@tUz4_3xM;Yy#XXw%_=z0z?hMO)l`bjTB}u5 zc?k|v^@lqqn$nZkf!gb8lQk*@1o+h=U)M+Wa9Z^reHxx>@K?PWf<~hXCf^p;RrQ)R z+!;72?Lik_#nBr%fXu|@-1+k{K=TZ+c=~Ny?RleuG;J{^*9w8CgIg$Xd79c$&Pk(@ z@gK0_OgDe%SSBtxvs!FN4MV;2Gh<7&Lcn!9FVA^oddkSysL^XSvzX5+yqEq8OC*+M z!kSDm-x~3pCyj`~YYi#JKO8Bcw{ltugTf%7I;06B#CjxZ!LQ*yS>pS@ciay&e`e%` zam`CG_Bqf$8vp~7SkD$n`UcFMd?$L`AA5Qjsu^??T0VcWU|1dCv~^YqkI!{R+TrQW z7Yy4Gz}dP#gt0yFnAqL!5~97tads~__>mlR8moeV04Gm}`&`8Lt%@^A$m6D;H*VyN ze_YH8PGOt|lTE8pxKnekzi7S3>-sSVi?wTk52w?5bOWWmjDzP!Xysk`8C>Lsjr#8^ zs@m71(YO$Ihp-~|K8Mh1TuHBF%>d2h;C-xWdC$1K@g|_^rj(tSmw@e?$NAOv+~cmZ zP7bkvM*f6C#1$*ygGx1N#z+I{)Unic3SqqCL2rTT627j#brvx+_m&r$7X7YkQF?-1 z+R7GqVB|~?`_1sw<>Hc{vLU2sg01Q5C#oOfc)x8Zd$!4|y>Z7?M#Vvat7IGtbduHI z)z2n^h+}dmKU<%=oURh&P7f#M1HSjDDarrZLGI0uTF##(IXAbQZ}Azl|1Oj}3l1z&bs$txdMm>pHg z`MNGcuMNr!E=caI|0-Y6KX@E&X1KR^SU#rS^o!Ank6ekXfxzG&a=)9_Lq=sW0&2`y z^#yGo>bBGA9kV6*w%;nFlpKXRPG&xAqwO^ZVA`vbd+nzi$!o+Dhe&oe{`X(cS~U?Q zb70}GjYl(V?2sZB1BI(ue(gu6axN!>zNdPfEff9dlnuH-bup(Rx$}m8KepQ@39|

$?^~8+$G1rfPM>!TpeK$DJXca!g)J780ZU0F*J|ARM>%>V+Ir^NHUaTLD>sA(m0zk* z5vMvsm6kpetfadRn4Wq~S5z8{VL~h_6+OnH`US^Qdbv?^#OlsBwo*=?FyeO+luyI| zffbgwM)-oxO(y}wpupKo8sg~IgglWS5A7mBc3b3-%xCn3??R4$$oBw$!}~A2gPuUQ zkE(Z;>*&j&{;%WesfceR%zdCG3~lej z*>`00p(`E-RCThIrJI(lm>}!=05FILaDo+#D;0k_g#=;tW{4h7YpxiI{#xB!=D=Uz z7X1s#aBWe}glTZ8tMQSDl%uWvcIDYunkk?Q^1yS+@HD`Bp4gt`5`4}z>l|e@!_Y`1 z-5A~@nxrG(-dij95k+@jR%DjfkoZKKhq^arE>H#dE87Y*(UNsb+$7r2eCQRVOe6xz zfyhGJdpD=I1(r7SJHPUFKX&ywGX9E{W|=9NCCod7V4S*YsH` z&`?LI3N6C2@wmn1z_w5AIev|Ssv|wcVpGy>HST72YL}UqfsQ+ku2{eAjFGAM1ScT1 zs@_s|xVpL34i7O^O$Br`u9-VvsQvWHKAz0dvRh4dAdTWx%X|gvJnkN3+t8Jc9*yLUHHrBKLgo=2R#$^Y8s6KDhs!61=-C6M1 zW8i|I&H@5mC1?rh?|q6}jJz+{`!ke(E6d+E>%x3}1XtYuCAkxiGjlF%y|lbW*qdUh z7E3*P619BOkF09zAL5@MuL>b+9xi3AP!a;2&&~Eu&yNvHxu7$Adr7zSiS>ND;n_gt zPMY}jvxPai^HmrV-#TJQkE*n|DzNKHI<_16^mTins%&`+9o}P(_{>B5?5ywSNxA8s zsuRBQ8`+mj|FFH(n9d#@r@*mi3nbzuB)ye5>_o8o z!}JCLd%+~b^t@z16BEw`4-g@7IW0Celd!i76N8~y(;9!s+SAED^y+_*y!$UP;{S|9 zXP=BQ-R{1<6&%9EO3L#E$-H?cGROgQnG&40c zWE4AZe3Hq4Tt04m;1%B6G`V2VCULFX%rr#TGa=mH z;;`2$E)5x5Or$eqi5NIG78a|V&zK_Re}G-3f1yw6+3^1Qc|e_CDC`TW>5;k@F#^fp zrHj|P!XxB}uWSzOySszD0}eF~=pY9}dyoqMb^C5=9-oCb*uzt<5M)q~xrqm9rF%}M z8GOaS*T2VcN>Xhi%wwTEbPKBNHQy98kuwud9s>ZDS*1(4p1IRETJ+Tg5Y1MCyfIB7 zTUG*+nq4g~%iQd+922YDbGMa!O2e~S63R-oZ$UtTc|QYW12<1ogyyuc0qnioK0_1y+iyn2Cjtd)8Wb;x_39(x( zI3_15C9d7shfT+Xnb|lKJ3h8CMua6#%TC-YEW&P>JX)jet8st#VWC6bT+VNC^>m%w zht+R&Fjr^>-0Ii9T^X{Ujk_XZ#s_2&c3}TwAXfMZ)NB0ZD(bZ2NG#UYOv_%T|znUK#ls04+ z-Gr*EPAh-tjwZA`?JDWFear+;WfY|}jnyaja|tPcEiAA=_QtzInS!vH$bj4~(b*t& zqIpxDu*`&p7{jg7Y9;L;{on(M&-W@N9a~J~emigb(z-fhUkewex9Pz`Kl7G@qoiYB z%{L{5u>87d6y)BoS+J^;z&@GLTI*7eoCY{~Hq*YGw!(KMwIDfksbvPqsOnDZBon=} z-p=l>2L)kPPmk5Cwu<~M9<@s+b%7{l4JQxSsjfM-SXBDqZMvshzODnhNgg$RelJ#L}vdk-`4uu^CM&IYy{f-jKL=<=yEXti%8IXbsG1j2@ zpCJ)Yku#Ty@K1#%Wa7#3IC(mQi-q$O@HToX`py`>o%oq8b@v>c$T2W#d z49t!9BE2Zo2h&d(;tm~nMbjN##|Iauy|eSW9T^)@v$(94Q<&Q{4toPtjpdPih_vbM zq(|1vQ%FdTQt93jsbNp~c5H?%4OxpC4d6VsUHBc9JnTIvhks3;kZ8-YbysAH+UAw6 zYJ0QZ{gFL}`L^LrVxdmLrIhqd>+tZ&QLXU<3EcPqUnR3=3bU*4gyUvHOcqiWy6v|{ zmcJaOzt9W|H?9$y^OW-ghoH4k764(%*t(euJ{a93^Df|;(rxj`GzQhAPvo-;lV%%J z0x)R}OsQmzo396_XsO+n`_pjjFVXHY^Kw?-`YG5A;ar$q+gLwB5BFG0b;a*G>gQ;G zM^zKqVjrj@$Tfc<0vcT2E}>1KIiP-vP-IKzrA#qNU22GRKZ0;a{Hl?a^{#e`s}-;V zo9)_(i582wwo81cADFKwkxNo3e&*wz_14^d9Tm$6K3fjkyXSsb8VWquKUh6b?%ENL zIhVlo&kD`7o_8F^vLxrrk5w!F=IVwu?WG>>uf~?LCIuT{birLadMxg=A-rcQV%&iX zN7At}=Lzcra9Nx^ox+`f!^)s>-#z0B^@9BUNiNzHvsO6so|`&(8a`hF@5Qz^?hfqM*T*?bc-l%W95| zV@4w-C3ok4FVxA(`cx-_+VB;-1(V$Oj18jK%ny9LNSmuQe2~zU;Uf#D)(ijf!Ylg+ z_4(dz27$HZC*$#AEQ9Nu+_8O0+)fXHwau@6p|Y_2BjFUa7RYI3<<&NRSpvGBPmCqZ zXa#fe!dvDgPUb0tVF|nLhoPGx`DdYL`%Utp(TIrpHgrRst?UTGj?hhtdl`<4iP;t( z&hX*=C>CO%_$mVoc_+BsH6qQ$wFqeG^gRFJkUY+`^vRRg;*b1b-PKlb=v)oZEsNaT zkZwme@0io2?ncP+>jrB}qre-<1O^iqr$MSR9>1X;r#Ka^?TM*EoFym4AU*j4LU7yG z!<8j=*Sz$;>o#1iw{-Ypjl3*7-_J1xY|Amf@%KcXgj~_`5{qTtTx{3h{h^81kr$Xs zs(-SKGiIE0Z$Sby!ybwh&rR8;gp}Xiqq@At;I}P7;57}BoS5|AkyWlV&ngrNbz-#j zOq;D*Gb2kNJmii6Hnr8g!u7i-*UHy^Iq-?0X?nu%lON#5O^~OXUYml!q$j~2q~=@1 zawQuz$AZYVE+^`Af=(6c@z7^NbT&XpufqbhMeLu;1*j}dJzUxxVhrX*w>`+o@b}b3 zB7bjO+YO)y?P#WjbI6)9YaVu)jNXCtW zPQ0m-jj*-gY}qw=*V)p zSrW%be7>9-hPe%ZbR~{2Or|ZUs&C$q+-Wk=#lhkx*pf74lrf(CQN2#)qwTANAZ19w z+TnJzT~S2Q+~Zi+2lN|JLW&_}1;Ffu*=zTR&+ST;LXnQEfw)PEhkOL=^UaZzp+$7h zc0O)phGgvq^^06RMspK=yxcr@9EVW+tO;(wc}M0e%s|x@_v_6n<hL7Mu>!rDxk^)K#kecCCS1JJbsmtgNi< zx?Yd=e}m?Io?H%8?m#ZdiqB+ylUKHAoj*JwV3D&7W8l(bx3NMc46(Hp`WwL~9}1jz zYAailVc9G?9GNJkmaAy@Yp*Cpuf0&qkATMfplzv3$CHA1^2?-fttz7vc^5q=vT-5m zy)>sTe0Jcjjy-QKrF~`92U~*v1kK1Crhj!C=5-M)(!5*N)e_d~-XrnB55_jz z)}pdX!RV@{;Wsdr9#4pBr7eE5NGe_s?6>)bNpaMOi9y*@$xJ9-PB->U`Ys^PQ#0xr zFo7ENqR^zMy??3pwyAiWq+Yw&+)>EvICglH>vkW&$3kjkNsup#)4ohcoKU>H+6HeG z|L`cM8`H1mD3ZN4mE|D-3h`v`)6gg_6}U^sI1{k5%Jki{yUD=#5f1?G$E;=Hxj!>T z0>Es<>)7Ejn`moe5ZTsQC`Mi@nZaj_AvBjpq;1uJtzw1S_tdt#@@UzsEXX6T+n3L9 z(BG}-azK6InufPPJS1`@3R=RVhVP?oEAc;i+QeW*7{7rQ=mIdwNssTHhPJ1cY{uOv zJEK2v>!J(F`-zU0hX-vhux#O__ozPY=mj7N+2lT*WU=)YJw9Mi(82H1F1o<|-)lo3 zV`d~oPHL8hple#|N(1C_5VKZ4?nfRA2$C@ydO}TGsD}``2M4J#nzv^-IT<|n^uqby zisQm7RtlA8Eai?YbSFC%3jod{PiHq8S`i%Q5`d%FZWBz?p>nyZ zRKDRCwKL~1c%k5`NwvH-2^kWk2MUW?(~qBnwp#-TB@=)z`46r2yD2YpZrVlNo* zF{*!YIK9dGN(V3(qJ1DGm7>4IK-!k@%K)}>jout8KcN)Sys0OHmrzbIu$C#mU$zP+ zp#{s)4ke*)yd8_iX4(f`fN@&V-?r}Pk{n8O-bdnbS?xQi4w9lq-$O-GlJCLg-5w?PF`c@rGU-Vm+2?0j=WtP6rp2L9Mz6MUGR4 zHLPQ8Hnc3P$FGbyJF#db%pRS~&UWOQbHR|DLOnLB#=eBD^MpI4AT=(M-h0CD1XWJJ z9i1GObsxIU=>lW}j|U?QPlBdfadmXk^HRkrKyH}ob`!k~re?=)o*EXJlrRxfgPIZU z&gf>66;BmENbqI3L??2?bED(Y{uSR(5ul(px%x!Xah%YYqwNMTR%p!4nsHeYGH*;m zD+OUIT+fw}B`GUjhovk-E=(J@Yy%nWq*WM*Z)JB+MpEV-@n_Fgmj)1o1g~iST_ANN zWl>>7lpyj$HYrZygD3oCR){{$35mt~m^F_M%ZUIopeBQTvHlPI{8a`*=&mY-=3w5(wB7|+0q^PF-Igc(_WAv48WbE>R8Ufam~6Q=3r!au zJ2*HnkYB*5q{LaEvxvnw1Q>oW3eXz_d)Rcq7JBllfPcz@PiR<)-%^wm)-&iG#5@ff z{VbK1s}>A)kw|L?n>;zYfIC~P^z@rWhC8L{QddJ19T-(QHv4x+yLXM8JdC2WCxk!I zOX#uECA_S+o4TWqVT@)BQn;IdtGs;>D!Q=n?so3Lu+hiy0@y=O|Al5Fs~1<5Aah;s zMf)WCzJ)orFzF;X)K=S!+mPZJD6?IJWI2~8G9C>w5?XK2_)D=4MzdPy6{GI1~DN$Qb z!zh}clb2I^a{iv93QEqlW!XlhUd4OV#FG}TIoM;+msZS!aE?(@U`Lm9sY&WM_Vgo2 zTCpuVMBC>z*|oh`c|3$RoR{OE&Kf4?552R1I`Q@Xw#{UatFa%ZX8_mxUV7O<2ax(= zlpGQBO8E1kS>Csj7tpfK#v#oCg;k&^bBln8nd!vnT>!&wR4w|IntLFZ0`m zeT}Y2B%O_aH!2m@qUaiL!L_eCzqZ0g#KG%kC8uKF5 zxdc$W3XsL8H3>aroUI)#^un2J>aXD(G}Y9qQ0W=c_`2JXeww$;97|&LiXnH%BS^44 zuQ)$9$jFODI^`~@PCZb{IK+kUA#=WU2 zx~3;(EO;$l7}MiqI?|HLT!irS-|D=oTg2|Nro9Y*oU#+DQNnf*H}n0YEe`hrDygrC zll6F*3>b=%T>)8;EYEfN1p-OD{@A`9u>d_iX0iv~@HL;Z&>&|HxhWPJ4kFViN;X`yGY@H)y<5#Nf) zuP9q;A6*KbHUFF=b`L?x4fy~35Nsq0q;JYN-WL$;(-)9AL?~p z?SSjbmoxcG-1pEsOU_Qyb7a$o^Au~Ug#pl7ss)eanRzDdA6m|%w2uxV@xPxckGN(Y zn1Cco1;zh(Pq)iU{mII>{DWm}p ztd#=ZxIU=}a-@b2Qq(adB=$!vuzb7UUQ*0qz%>Gs-FQz^mwnp?Lk)2qL^~|l9a)?V zo9TI6#Usp;&VPnAK2krJPRTny<8dFOZ(24!AB&4$`gjE+-o)Fkoo#PVGU`g4oVtrI zQdOh7*pIcOXK#AN*D;;`@<*qNRqz6 zM0O~5#>s6-{QlzyItp0mRf@FCCxjo&O}7AmPekJL^zRVODo~XxNRzSg$2ko9WhnUT z%Lr?@#ITKqFe0$~SvIrqmpPR25%c_^{CG+rQ99O8|Mo=Z-JH!9@*2u}UN1VVaZ&;? zcZm+dbG4}xJ2s{1@3}M8dcR1%W>Osd&I3WMb`Nw}Q5;}Y_H5`$%FloC2)CS|8F4Ot z=BzIx#~TK~2XFav+C>|f+izu@TEs#ZR+mRHdOBb$Toe?y zGMDWa8hJX@y)YQ>RQ;Pq1MC!WWmqQWA=95H=qHoDwmiSYvmCG>{T zs}5Wj@P%ecApBkd#y|5V)NTdNM3odYUUU+JUUS4JxX^U@p_alf;PnPkJ2BAeI=fNx z^I3gk(cqz80~#+awf#|(OQ>(*{Ri^uN;(9EM#>C5$1Vn=y2qqM4KOo`X9%VE=~5(5 zFJFT2hMuFx^XN{R9)Y#(WH|LOja5d>`!CAlrI>}45r^rxh;b7 zN(XZFtXI!_6#&$Sp#7ZS5-;sbUPC=lq`WrxX0ngGBr_UqpRR`11~D&<&wJeWlhXQL z^4;7O!gt{!(Wn9?RJ0?qxtXm4Qkk7Fq$!TA>(E;Lg&@h}LK#ZS^q zxA6M(kmDReoh?gUVmg=24ZFU+QhPWf^4NBUwzOh~+MqIySVh&H(mEPCHX*>S(JbXx z{{5x8A8+yVEa56Cr2)$l&TNxSfO%9;O$$fdCJc%Nyp_>J9}BM4*yjDG(S6S z)Ir(h0s?GMps)jZ-D*PY$3Hu*#Jr^A-wE-M`ho!4*(e02)U?YMTMui@;uy0|Ui|ft z%!$0`7l@{$1sOLDsX>+IT}U@VM3TMzJMU&*JQv=NRrm`F=_J-z!D;eZORF(Sy2{@% znEvIeO-oVTRO|G3=nL+-&}epyUl)*&82wCYCc#^iGFzs_bgENh?$d6k@pgVI0xY6E zA@g|2kE>V{lMy!U!_XsrJsf(ar|$kLVxAx0(z>07-QT2mEd4XiFQ>plmd#}u`H?Ap zG}Pl$DUMHRoAG{69`Hcsm18XzCoeQfqHj42ihE=efbr}ggK*ieOjyB(12{Fe&h#bC zOzBz@@27r}EnD1b7Y^}Lq=nN{TC7Gu4JhNJT%*j^=!rAB$(KsKeXT$ml0F1Mw@#p! zC*1L~i^^cX#(ob(ywpk_uzK6G3n&@WobrUYt6hmUoCdt2EY^u&A;trW7sjnL>J?pz zE-?{DLt;9(mb276E|s$TdsnwN)tA&G?4O7_os|UlL#x9LSoXJXTp(&!puk$8Z*4$Y z@wkg)LW8D%T=eEAw=UowXW>Fja&kveD_KeGY0Q1gq2;oZlINw61&IG~=!$y4YUfxD zZ)BvuBZ(5_i!oKSJ;~I$Fx&$L$dh8W8G^TlB>b&Tt>(=f^W<;y|9m?(P*@;5IiHu4 zFIpkF@-Erm({hQC9wAhi*#eH(5hoM1@ zXd776K?y2Htq#XCPBSNI>G&iY<=Bmv^_R)SkYZt;3=~G?U}ToLi)6Q1yK6i~32jLP(2C zcD)_7Y-cshSJdeXvas7^JdW~L9^;#OKvQwzhq&o`+>H4pCr>9QHIv}uP38@7ZvxU_ zG~}i)rqr={bYx`YtI%*xRALeC(u%#D{uzk|Sp{m-bxv!xKtOz?SIOWntc0RszvSSq z-E3)js_4RP#j0gjbaI`aH|TR!3%TBC2&wPSHspWDkg2~Z&o99LA;w|tF&K4!hPi~I zNMe1bxN@zU#>?!^Kk?2xXGLbZ4&dj0ts^QUy}$>i+TB4z*4n0wQ@e!5;P^)H7i3ZkE>tf`tW&6= zGe?>4;DR^*-7S?`_&(fKu~EdC#bv<(l0$UvIF;1$@QWoc{J=nrY2a8ajCKMY`kMOq zOz&2Sk!HZ@1-k2RMllbZm{YZ-Hllx}9WT58^fnW^j4zi-0_krIq7f<^>j>R7X-H)% z-TR-#q@FxH5O#0vnZ7pjWbm&W4mcJ%jcY*sSSE5&YMU=s$$D)FE(bYobpK@_o+&$R z8D!=s<6ZyN_0eT~Za~txL)l0ikyq2AzT){+?_R_d2PDlO zMS~>0E2e=Ayd|-4AYVyM4b=~AJMmjKW5=jw02lk%tsE^D`tb`M`^#y~D*FCsm}2X$ zd!6rB{+lFN~Pfx8R-9UTt;M2J+ z-wSx1IaDQyOdLa3YNPZPudh4CGRv7Thr@O-QkAZdJE6*k#0~2)wsrF-OWC>O@VW}t z#jwro=72LjdHFmM&$mqAt?ej~jFSd}VD+IVIw>xsCWSg>NbJezm|hVT7Jsd~e_)PF zbkbY&oi3d%{2r*hO7_n#lI&}9h1rJ2ql!$_6GY_4lp_GCBLe8dzucxPE`(rvA!ZWHztAan zK(LZdx&}4&U~UYyew%>Z+!m$O*kagdMqME4BT=jQdb7vyW`P&W4Y8JCjb5e)t0{bj z#fBKqSTN-RFFsRP>r-oY@W_Q2XxDBY%lhM~y)pN(&$qbgn_@>pai1!2sx-rqYy?Sv z2%l!X4@kyu)VJd?@#YGtcs7=f%B_2;G2VxQU7-I z+I2@V?S9$6U+xT>i5P+7;y~r zu|DQe9!^*A?Ey)kIH-wXMUnUIPWOf4BI$|pLRoi2BwVs&+uP9rAa@{Z@wP3#0Vl0_ zN+WuUbf*T0Mz#~1lVR==l&o@)-F`%bR-?v=lNefYumnP8Ti)Ow_D#D6JT8v+YUpJ= ze(n**XCPjIk%&o;4K-qqNSl`9ila|Nhu;0OCs!Wg>ATaU;ybljcJu{K+uBI2LHGpI zMJ>(4vz{wemCL0kobD77V3%90a`U$Plio0}(}XpA6QlCTAJX~Gqf2mK+TyJ&NxnWu z@CLROb7FgSESrz=-S%7sK4BC0sd!;_Hf893_76eo4|R%T>qtap8#cgxe3wd+y=$k9 zd&JI80kRtNEF8)m_h*xyMUyz~$8yj}t!jt3%x%fUP-eZrZlPXU!9Pc_=QeNq*QGnZ zxpDj>#?Q)sU&{Mbe6f>nv+=qO##7q=Ho6Eq5lwu{Xet(uj$VE|E-e0vC5LF2;Y)@uKT}zPvc)X(Qn`D z>FdiLEho$$k2kE|+f^dVjW8Xrf&<&Sj$MphtAGSTdS8}+> zzMy>Y?L+RWn}oNK(Ef-Kwd-gfSz_+^_;_(~v5g4NS-T42{2uFu{1O?}->*psZIbm? zMv=JYKZ^Lq^7YJEzogvvJV>@^q}ErL{hk2edFA+I(u8hYZfNJHo8w`8X`Z04q_}kJ z2hXvErN|}W(h|dcFN;>sJxti~(qyS2ttqdFJgp-C#(%Ht%gc(XEdE>97ri@IH~w6C z>mQ9zd#IMIxK!S7^G4s638rQTD_xsmyw&Z6spjS>Ovz&E`e?^*yki2VZWAf4O2q(l zyAy6HE-d^oXYR)jE!_ma=i!}Ji}6A?JC&zZ18JgJ<{$Dg)U<2J>BSMqmvcoq&XA|; z3lWPN7L>tl3>Q^20|8U6YoG8A^XW2Hk70WkAExdbMt>K+tw6d&e{=r z-;>mFHBOM4OXjo_Z^-@-Yv=-oPz@92G=#d%91Rn;*wSvFZRGU*Rn zoGeB;gJC1jH^*BfdM2Wg>II%}_5HroZ$UX>1YIab*|)9WMY%YgMlhJ3 ze5~O``2r6=J;3Laa7)5wX=O}QR9L3CsOZYci_1W5Zs6TZ_a>$pog{tM3J#Oe0z-@j zt($(EDCLpr--F~_kpAl5hjoI+60^{?4XGt!9w)6d1ky~XNS%7KljU@aS?TpZisBkX zxisdHlV_;#e(t8;8z@_9>Zo?+J)r!X2*tXgqwLL%XLX;fdavcKUQAT#JgnP}?b7sN z2pUtE^G6Wan=EG=DoelW$t@uozJ`}R~2`OoxmgfWY^Yrxl;Y(^d0$G@oN)L@QwO?6p3NbfoI;;@A3tWjS4^JAC!>4 zq5Phl6l;{var6D0+bczGE9T!QDJ+xdP;qn2hvJskXsJkLuLO$CE>3rVaf#FrWeC=U z2jn(YbG``>_Pps9U{aQe7;+kW9%rOE{tm^z-`fM8O3Kg5>ag2&Qb*f1n88DOcC0KM;}3#Xy0*IQZd-nk`pFCOLu_H~JTKO# zQ`>LPIJfME?HaD^X?E{$#`gMJcOui38EC;4e!0K;xsZ+Rva_!E*I_%qw;$O0pX_6I z51F?xgq6x#Q*z^7%t%&Oe2RiK<|J$P(A|IXLTWaH)GE?dA>D-7h|`{b)(b%S|IFGmH+ecv9ZDU47rWfkB%^Y<=YC7`9^N%T&=Yx(~X>b&SXgY@WWb|qr zoE`tVv|vicNxH25htZG&>f|O3@`=?w6rMjV@T8lvw|+FOH(McAhGMVWd$~Nr(84oI z+|0>^+8*AbnGtHzt!r2hsSqZW08E&K(a_q;B904G%)Q~HB*x^(h?N2) z5<-l4S&BAv;BbSrVhF1l$H}mT;-=ySmO7pq+TL^yE$^sp^IfHna&cI;xEBee_WAI~ zc_x?ZR^cG^icSr+zhGm%{FOgCr$TYKLb*Hrhc8}t?QrKu=U zQBY7&DUsemrKnVu5_*&_z4rtZ5fKp)A<~s99Rf%R1nJUy2`$oFNN7n&Lb)5?bM8Io z{O|p6e)sG7x_^7kUTbFUnP=9XdG-Z-z*?rUA>T2}k#_67L~;wz<&duu8*e|Or> zio7k}Q}U+5c)%vXz%G4r*~GmSlQ?Kcys%kgf8gKK5?nI&*ugMaYi;UY&MjH-9GyjN zBx7@2H7qsXKqcCL<;;>Db$WXzdB*?Dj!dAPpSkLMW9CrRR+K1v@j~qA`-CT=(voFE zz?rV!gs6hJbex1Tr%lHGNO`xE;F`mX>=N(WP!|nRm-1ixIcbxnu8y5mmAZRqj6}9n zvUo?>eJjD0oera$5_Pl7$K%iR0Sq11iWGab)25ERgcw2#hLGT9rd?PwTOj`7-jKR0 zP{)>#0I?1~C90KOCp#&Z{FMH~%F6|Xdq_O0L!>IZ@Ptc2K_1*rEoU_4q~#rP!SNd1 zJ&Cev1&L2eGap(GIA!PMRS=#zO3giSwtQMpSfRmtZ1EDV;w7*ve13I`h|=`N@fKWm z&`xrdW!jR-uQ{)(VVMl0E7}2E-yRhdYlJ>Mw0F>fi{7j{JO2(&h_2&2byJtTpn!hV zYdjk~+?z$K0HZmr#8|2u^}f%T`+xAJB)=8YFVdJ#SnA@Mi=fn_3SEB- z=!#f4qW3>N5OBpewg5HLJs7s(E>6)Nf5o~@e5#>%4|jf&OYW^br(L{3OlVV2XZJ&t zY5c<(uF$C-Jx3os*u!ob;dd@qA?sk>l572#k4kUXiQ8_yiIp(jM}u_N%u3H7g%CFv1m8T^PmeQW@x0Y`h?&+ zdRPQ}0Qg&e^tnhZQ_E(Cp~U2v=p6;o8{jDuKT3SNIL0w_!E_z}F$ejpOM0E4d&?QW zZ4vqF#-_7o>SKPqkN4qN4h`|ZIvU^gSc}!n?Z!<7vcK@z$EiLLa#$M^+9jzj9(|f_ zK|oMet0(4i3Mc-G+6%AgQ&dJzy@@b2h}3;{&ikj+&>tYMZCmnn^^4hj!MTB}B-}8~R`SBwUmxf2X1g1SjhVqLst1Lc% zHS~<|vHGFYUpeXN%xi{tQ&`E<6oc>6`IE>=pH+^FfkRFT6+^8LhQ&o4LdU1-Z%6um z?_RaP^0^--nelG6T{VSe+ZI+LPNtH0BQND$ic_pvZg$`OqkQ?uHqt>q z*m`?dU=B}p<&0EnpzQ~=Ia!Q6`Qjebv}i7>;FA_!Z~vBSicfcDcEwO>ua)p;H|G?Y z46OuV+CgcrkfHfZD<$NW;7dD!iyHN#q!rvEg zRnZ&`w^ERRzx1)4tS!7VWTZ!3Nc`JB+|m%{&^?p5 zRPPb6*;0ho&8M?%=Lao>HK%}kIxn(XRLjE`rW^(6UD3BOyAN&fCADjtWW8baCpo$* zp=dLwi|f;w$FTlA+e~{HQlaaM&=mj;_uvffKt@Rs?Lf)Im(7T|`CIb#;U@U_QnMVp z_BCGnElfPsT(uDu_rliJr;+7UY9~^eEX#RUr%&UmEq*)Mv%!P18lM>}&9MQrvYOtm zZbz7Jzftl}O6&I)q`S5Jygk+e9t8+mg2hB!T(Y6rg|95z38aK;;>A!&8}4JDe3`#o z)A`YJDbVmoo{98MyIn~4T48nnu-jtW2qA{G-O$zSx{?C$O?cG%ZE%#5f5M?+!0$B= zrmtL~5x$k%o{T+4mZ1dkS0b$MP*om%qiz!TAPr!bM#*taTEVbd>D#fQmtE{4s$N+-B(bd^i^C!YW=%?15Dxt#l5Eooo@+T6^OhakUBZVVe_ z&D+$f?U_bUw@eGY6~k44fo4H%V96fEUZcRLrE=gUFOU5+poUv)wUmaSKe z{5nC+)U5O#^#dR#5+*9P0VGq9iYPltQdoH)ojU>(JZPUI6rZxBaKzY?72xiU(wS&& z*W;IakeYuM)+i3;#TLqo$D$IuYjt|rEWr1Q%PPCKdD918c5#Y%T|(;iE_CwHJ+7Z7 z$|~CffI%eyj@?_pER(WG+%ux{`yxHIR0qO4(%Z|gsMk*iBj{#V@$XsUJN`h?^sP|m7CBZa3 zTw(t2^S&BByKyIg?JxI{OC*AL$UU=(0)`vYP7D znBDYXPU_0XhF(YNRvRWEqW_lFIQOaRfj#UNAI6Y+<#lLC5l={Z30g5<(6@_P)yDSP zajh0nu6-_W?@Q81wizfIHr_28(;6?e4aTE}ntP`91Dag+le61;PX|K2w_SR~J%{$! z?Y^#a7n1s^GU=SXfb#P%k!>b^qp#EboKVT8d}(Ke&Xz#t52_bk;cKR|b%F2KCm_nB zllIDvW?z$;u|_p5`LbqD@*bV%_but%&kd#BGF*RewEJja?g)RSlZDFt%~Tc6VS?VB zKatNXn{7Up?Az^Q1$$0=8rkWXWV#mV^H%xcZP>+HnL?c1*-!NoR1$gAtnvcS&0b|V zwQ%uRaOl@rYCaNY*t^Vd)3Y@=c^zw)mg2G3hJCNB`Gs1-zQE>Ft<1_<=;{C)pBh6Z zTNr)u-OCgQeRYcXP63KQ#wnyo>DgXOn$MH?BW5PU9dSh_UFR~A<`8|9(0bGzis$(6 z9KAM3lby}sLDCj9Ct&Qx-Q7PjE?@ue$hZatxR#0z8o`*pzCO2frz;HPS28|%d3m>Q z-`)xB&}O;H$Z>pa4q4VXy1y~glx6NK+Mp87w&HmVWE2Ec&Rki$n8&h$Jn_4sBL>m@ zfY!&F(_8N{%}Mc)A#r0y^u+xWQ>9S3 zO=pKX(Dp!}zf0Kx7O8JG{o(R&cf5ZrH^n6?DT6}p)DVZh_n zrg4Fep*$b=IGkgjPC}^j{L}?#?q23QkH9wVFl4Ps>ZZ;zH1yaW5^PbSRln0%Q7pkd z96rW#px{4f(t~ux$m~K7iDYEi=1P)haoxuGIzJx!S^z3S4&ofUd)RewwdAY;A=KC# z>DXYz{I4*uMGBhg6?@&qIU*9B|ZiS zZT|47qyd#Kb~8P3cgS^~u4@NOt_bZ*htnMbD?3cZ6UHJrT!Iv0@np%t+1P5zhwZtP zzGB7`%e2MGPS~MF?9LMS6Qqtj9(ia(f9U|;r~dBrgeVrqm#yp#AJ*5=sZp~Ur)3T* zWjwEyTE7rlzW(NF%TwDsCrcUqeIJ`NO5raXY5LhE-Qc?~G#;UVQjE7c!=vzs=e;$= za;z&X;)Z&{wTJi6Ox95{3`{(>vhHtXs6cDccPMGq+rYPCYRYGrj@XfyC@S*tuGl0? z6TQ(()|D((bn9Vd39+C%+dr;vxjaul=X@a{Z#etSgcq{Ks>;LM8iw1PiTQ@0jQh9; zSFrfj<7pblvobx2f^l*_oanjKvKhjt3tT z2h(>=U+_ttk`34?qwDfar@}Y99M@)0lL$iD1bYkEv^RA!EiN`-mWC|$^|7eemlsl= z45+9p+jnqWTiK73kFHFeOgM?bRZm1$a^tAb?R9aFG*+>XSOLAeTm?dr1U)d*8-XI- z-mWe!?bYdJh$-NRO7+P1?6qnmS zxKym&s}LA+4{XWx7=30# zfNCUeuwiONUP-PFwsfu2a6sQ2Qx2;F%ULH+Y zDvQW>s7OS6v&+T9(-aX|DBXG7k6%@7?A((Fd&~W}N!&Q1?uMKj^;X#RLapiG! zc4d+eLeLuqq4LG4N`4#bv(3$+NO=g)GC^JdgRY)DM!{YU^&>|J=)}RZ`m2YBvvT{N zOO|EZfikY2LpGPK4?T2amb^0jViO;$n3;-$M9`~I5|Xl_0X0MNZZC=qlael9yu_Vi zl2>S^7sjE5Jb_a&XWPypmXGo3dfHn*o%Sj($K~X{lEN$b$B*i_yyoy;xah7d@P;Y8 zLr<{b!_0~iGQ6m;AUE9ddl1}MS%2ii?1imTp%m_vlUk6-U`ABlC+<%pRrMxs&@j1J z_^dT_)NqOa%{+^%%?oes!;?dclBAn&!)I#7xXV*JFcp&fp+sjzIwRv*{*N5e+3vkq zH;SV)#Shf?J_T+bn3A@8xcEX3{tbfJo!zJ4d@BitNo5eD`F$tqOy~N#oo|n?izk2a5z1E@W~h|?`-*eV;uGF z>%ja#M^_hW{N}*TlNoHWx+}IZ&>PW+Y-Ehrt*VUljaLuPGV&w6>eL!IIls224K&{` z&z|x{)MR6pFsa!e60;s%G~N1?fJS)~Nx?08r%9Bx!=%sfkz@9_tgx$yNxZnV2$M2p zK0NaVp*Vhqfb6kOTP0*^+*ue?z9S>kS1h19n$A;RbeA+3Q)yZ_Y}lp9rFC~Rl1qFvKe@zmrT!(DMlLvo?_nm2;RdYLd%B}BI#oyJdKMTsk80YOXR zqz;VHV7qxE1@7cZT>h!qjmcQTz<+FS&AcwatlQyi1GApJr#pRo>E~c(Zv4?PNHd+9 z{YG0bNFo3p@Z4A-{BK*O$95`dZ(rx0aW_8q@?nO^!pFpRV@cx5m(-J-BI_YBy;}|3 zlGJ$~Gb=HXyvCAm*8Hz4Pxg8$>+1dOpu4CgLj-e9-1Rq6IxVQlthXrounY5Yrad<- z>pbbWM6{|UId`S5TJzpmro#0?w?9tFi=VC^UDVE#Co|C=)3D*yeYt=iZMY8&!Awy# zneXjZe1_NYZ#8atV!9fS6w&+iaD>%vZ_bu#NJSF5XaA^0hip9BEK%hEMqM|Lhoc-_ z$;KqxDlBE|o_SrC9jp(YM*KnZNq{$qqq|td2R#u1-ZnJ~*9D;XypId|314#CUr3bt zzwq$rsXbRRIIkDbs8v)`zE!#@lmi=bAF~ zI`cQ%H6804+OvkUIyT%Tt+AS|>b{NyKtm{Y5_y{ktGr4MX>DOJ-dC32B}(RE-+ zN#ewXkAg@+LCHF=amR&dGqR*+_Hkr3xQGc!AxcQP*BctnQ{OO2`ubO)B*eejEzavY zy69W@I@@?Zzn|)Rxbc~dCx4o@aLHbSY@Cl)A6NO5LeICUqRXmwtg21kq%%I?A99pB z?_aZO6Bq^dC!NaMC|i^@{TAv9?jA+2dFoz!1*zQ#mgd^`4L|a#trla>eLk#3<0&SE zhHopqE^Ox3X4F&3SjW0PblElyTPa7JcS;@G)yMP^m#ZgqTewkdB;oipm&p7fbcpGa z5|{NeyEMe?=irNK`h#0}W_W-_B-ONpKT-9s!i1m!#r~|z9UtKVCnTySJbs7;sm0$8 z%gimq4949s_TJ(gkyis2etgTxDp;d!a$)RBYEs{N1{kqmxjadFQJ2OdS7++%;&ST- zU43c3@II}kJp8)&AKQC=2X7T>MSa#lnKxnOOLGgR2SlsC@)kjsYs)LWz69rOX+PS^ zJbTbBkz+@nEWG$9l-RhDL-}|~Uep*kXq=E9z!i?UZ1H{ccixn2AlWI@*ZXb0(!s8k z?;3ZxC><4f2w&t^CEpmbRwtA-Oz*I>tQs^1tq5Lm-E@iM7#`G;y(Z8qHaYViePg^y zW%j_~A^{;XoFsQw+vS}$^CGdb!1x;OH+z-pVOaW_8x&jVNRZ_sAi9?qISwcC6N$mM z`CyVk{%J>sPz(Lx=r&wra667lrP7o~g{0m!Rct!fD$+m0jp05^GqHuE@{mGF9A|Ye z^tQnxoF%O}74^8BX_V_ia)(4;X~9|6|w4GP~Xp2g!baz_ZQ1 z+TEIqR_5%c3`1cRrF(2%WH=BNL+O@?P${cJy>#;eVd);~C8a zdi=DEnBqI_&UHI{((d?IG=f}~cJ#R2^PUuu`2EBcCpR^u6s<;cQntTn&X-ZRO+x2&^BNL8*?P;UKvOs^7STS_Zxb09Re> zNb!J?L++Cq=;}kg6R!2JbZU z?cF109KUN6tnXeZf-$t=*_{h4x~=tl+X>2t)cP`(%7kA^|6G25IVxCgyR&|O9WB)4 z%#Vuxb1x&`0~Kr7erxlL6bH8LXf$JJOvXKO#O4A8QeH1Knj-a&P<2st0*csNNIal5 zu?N?X1S#*#Pxh=UAM8?y^##h$|Lq~XQ9(9o4w#90G|Akwj2H^37oZ=>Yc6evPVEH?J6x~@-Pe}` zXu7(`%ED5WB*u4S2anLBcE^olSfFC+_X$+7cq<|qx3^iBik7xeP@c0GO~)>6uSriu z{gC2j2D&7n(*QABTuY0wq|z1TSqtoKlrEiAcUW_%8ug%lU#%%haTxn-E>>B;bH)&0 zt36)bf#Cz^MM8B}JJS#Iz~l>v6TO&HQa2bPHQI)ECLuPvc?dYJVWiq>VX*j3NJOo8 zh28p{0KtN~E!WYst^*Ss(04U|4}&7Q4f*>VBN#FPA+EbK$rXdR))s|*9R+ipES%Wg zdmjfU79O6%-;ys*z(#?MQN;yvx-h9Q0UFV8*y(&&4#(s2ucw+*QyW(K_<@A^SFD*$ z+QP5hoX9v1!U(6gEWO96R+ItU-4*ePIbUS*-lF-(5KCQ8!N74>a`o`rbzTjfQHzgW z9w|^p+_JD_sX+SUo)aD3)4#`WvNC{vzC?E7W4kMtaxtkcc4=iN1Gno$sr5+d;2I&w z&3j$#xfpx7zM<5&jnDER^w%(E>+OVg1r$IYeXIjUPATXj>>I$xF}7(VCS+sHru7?* zJ+*nd&tt%#klA))a>Qc(2Ob{)%lek}TFgf|6k~ady#>#N&9K1Y)ux?XSKih=;TzgA zl^bF{B`p^Ra1=iydc?u|VXmjtZCsh_%zH2Zu$k+Qit52+@zGR-8%|p4j9BdRxW!Z; zcbLqqEu|FC0sPatfW3Uy0?6l)54G}u$%RJYRk+?4x;_Qo+{yo zgyhML*_Ewj(%E2eH&)sZ#+C-c;`0^ZI4#&g_Pj-9uZBuAM!}Dg$I+ai!yXr!b^-)C zwG|KVUn+ykA)4z@2G(gK33v_|VryPgvWUCPhw%Ptz-sbs8NpPk5gSjZ+=OfP| zx@fePBrg6OV7Husbv!qKmiPbN)*K}^x+=XouE(eFShu5e0MM9iCnjJsp%KT+7qn!~tmDmiVBV{mL8TGiYSxtr4k-mcdAB9-uyIlo^iz=Paw&rAsN zq6|pA@fEw>X>>3C4PmA8xr$~R!TUMG70}~z)tyRnA&{++*fO}Z!N=T_kr)<%jem(r3*9BqD~JgW4;Pp{{*e`R8;=hhZMA&u?MWK#0?*Tvftg`qKUWpnOG;)_!w z)^l)9oFCjXY<5h>R5V?IBsMeulLDAN|IiV=i=LJ zmUxvl{W+Ws6cXUyfr&j2P$6F}gL{-p7RUw0NC(`rkTFN1L}wPoSULWNP>oov;hGO# z%~yIEX?`zNvskk6p;^MoSpWHEO9xpSv-$3LIA8;E{D+#X=TEFoMb`FLI@k1D%nJ)- zdrDA|0VABGzq`9hqQ3H=pUVCnN;7~TT4b-nd*>A|J+p@C4onF3bRF0W|6*i7R`pxd zu;RpcrA=K|c5AQ`4+iiv!dIT|^n{l5>Hs@SSfF;0*JQy9H*khUFt3Yp`IG0f(p>cJ z%w(LNXVtR2zz^FdeC&rC2MoxK4~mH{qo#7Syqq`bRtbSOq)hZu9}yw$zkG0{-qWDR zH)T69xT5-y8u8((qmpEv40k@nISKAEsLCc2-%5x6HFxWy-WDR^0}znz`AeZ%>Nv_#Vw%ke<%|GznIp`{Qj$nX<`x z_vqqrpI)ag!p2HgzCLqmKk%9cJf}Sf8X^a+wk2Ncx$Z>$sZ^he?*Sh*6Snef%R>4~ z8(k+T-ClnAR+Iwb=^~A`)$1!ts)2I8U5bYI+R3(Sk;{wZggmq}8<@LOfq&#>K?YOC z6TjSq`*0z84rdC~jVFCjx+%bEjc^?F)yN|r{-9NPxB}UP=MV)4Nf83ql;O^{i>mEY zeLM+r8Ytl!4Y1olM2%X*t0p+Box*kvTae~5F@W8kH6n38w1 z*d+-KI5`aWt-a=3LeM*S(>y-%liDrhJlv;4-IYaP_|=)9?PjYmd1C3y)iTJ;xpLYi zPoAIFBhg=T2s!cPqBD2T(C;yV&S`u=5jXt2ecVNr0H0lF;(i*X+r1aIR?S-0TW}`d z-3_G+8wt~=OovU`rJWJdlS*9oMCob@B{eJZm0DeFuymZtwW)~4=Gby}+b6>}TVTKj zcz5do793lTRSL9`GU+|P4`xgr^mievbvBfmW_9kxf6yn?KAGh$w$I(UJ`N{eK_fqY zxy9Tu;_%{}L=PKjEsxCWI$K(#PMKU$lRWdKG^*w!oXlHXk?N?}NSMqCyedCC6?g)4 zlLb{A*|<{Ey(Ha8nzUylQf<~GgB8hKGCLCA-m8fZS02<-U*ou^2hOC?OQ*F?cZIju z)xV;aJp0T4+4gH3F|CJy0c78ks9`DHSX^<78EUV$j zkz37#yaL_I-qjW!&RF2n^iqM6DNZ5zQTJ~A2VH_WtCiyT?(*#8oy7XoF?c@RKW;eV z9&ohJupmhvPoKa-ANF{C-bFo0IT!k?V&{|&2*d_tK7rQS587w$vD=vXodEs*dK5hW z9oqi`2VRZ-L6+=*LP0;7|D&_mO@*(<3S~z9_Ur`)y}MwvVor3->bfvy-&jrHR=N+7;&{8G9I=UTHOr~_!kvK>lJ`K}LnpX5EQk#x+>3GNwt zghSMjm3U@H;;*n(%L&^9Rq7^&4d7F7qIM`v%xtda%&q(BGL-?+elh`wCl<=tbP*%f zL*5(*@|52%&1_)NBFg*Yz()zvi`rO#P~e=!G=Pndkk4a3j2%zf0Fb;W%mn_)VP5?w z-19H%{MV%CKLe%z83367|B@R1kCy)Ro%#RTOC?A=J=opZ)6K)*=nSB9&?7Y+)uQ{) GUi~j{T~8qZ literal 0 HcmV?d00001 diff --git a/img_2.png b/img_2.png new file mode 100644 index 0000000..e69de29 diff --git a/img_3.png b/img_3.png new file mode 100644 index 0000000..e69de29 diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..402ff95 --- /dev/null +++ b/pom.xml @@ -0,0 +1,45 @@ + + + 4.0.0 + + groupId + casbin-spring-security-starter + 1.0-SNAPSHOT + + + org.springframework.boot + spring-boot-starter-parent + 2.2.6.RELEASE + + + + + mysql + mysql-connector-java + + + org.casbin + jdbc-adapter + 2.5.0 + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-security + + + + org.springframework.boot + spring-boot-starter-test + + + + + \ No newline at end of file diff --git a/src/main/java/org/casbin/Application.java b/src/main/java/org/casbin/Application.java new file mode 100644 index 0000000..ec4219c --- /dev/null +++ b/src/main/java/org/casbin/Application.java @@ -0,0 +1,26 @@ +// Copyright 2024 The Casdoor Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package org.casbin; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication + +public class Application { + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } +} diff --git a/src/main/java/org/casbin/api/CommonResult.java b/src/main/java/org/casbin/api/CommonResult.java new file mode 100644 index 0000000..a6406ee --- /dev/null +++ b/src/main/java/org/casbin/api/CommonResult.java @@ -0,0 +1,96 @@ +// Copyright 2024 The Casdoor Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package org.casbin.api; + +/** + * return Result + */ +public class CommonResult { + private long code; + private String message; + private T data; + + protected CommonResult() { + } + + protected CommonResult(long code, String message, T data) { + this.code = code; + this.message = message; + this.data = data; + } + + public static CommonResult success(T data) { + return new CommonResult(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(), data); + } + + public static CommonResult success(T data, String message) { + return new CommonResult(ResultCode.SUCCESS.getCode(), message, data); + } + + public static CommonResult failed(IErrorCode errorCode) { + return new CommonResult(errorCode.getCode(), errorCode.getMessage(), null); + } + + public static CommonResult failed(String message) { + return new CommonResult(ResultCode.FAILED.getCode(), message, null); + } + + + public static CommonResult failed() { + return failed(ResultCode.FAILED); + } + + public static CommonResult validateFailed() { + return failed(ResultCode.VALIDATE_FAILED); + } + + + public static CommonResult validateFailed(String message) { + return new CommonResult(ResultCode.VALIDATE_FAILED.getCode(), message, null); + } + + + public static CommonResult unauthorized(T data) { + return new CommonResult(ResultCode.UNAUTHORIZED.getCode(), ResultCode.UNAUTHORIZED.getMessage(), data); + } + + + public static CommonResult forbidden(T data) { + return new CommonResult(ResultCode.FORBIDDEN.getCode(), ResultCode.FORBIDDEN.getMessage(), data); + } + + public long getCode() { + return code; + } + + public void setCode(long code) { + this.code = code; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public T getData() { + return data; + } + + public void setData(T data) { + this.data = data; + } +} diff --git a/src/main/java/org/casbin/api/IErrorCode.java b/src/main/java/org/casbin/api/IErrorCode.java new file mode 100644 index 0000000..581fe27 --- /dev/null +++ b/src/main/java/org/casbin/api/IErrorCode.java @@ -0,0 +1,20 @@ +// Copyright 2024 The Casdoor Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package org.casbin.api; + +public interface IErrorCode { + long getCode(); + + String getMessage(); +} diff --git a/src/main/java/org/casbin/api/ResultCode.java b/src/main/java/org/casbin/api/ResultCode.java new file mode 100644 index 0000000..24e779c --- /dev/null +++ b/src/main/java/org/casbin/api/ResultCode.java @@ -0,0 +1,40 @@ +// Copyright 2024 The Casdoor Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package org.casbin.api; + +public enum ResultCode implements IErrorCode { + SUCCESS(200, "success"), + FAILED(500, "fail"), + VALIDATE_FAILED(404, "Parameter Validation Failed"), + UNAUTHORIZED(401, "token expire"), + FORBIDDEN(403, "without authority"); + private long code; + private String message; + + ResultCode(long code, String message) { + this.code = code; + this.message = message; + } + + @Override + public long getCode() { + return code; + } + + @Override + public String getMessage() { + return message; + } +} + diff --git a/src/main/java/org/casbin/config/UserSecurityConfig.java b/src/main/java/org/casbin/config/UserSecurityConfig.java new file mode 100644 index 0000000..97017ed --- /dev/null +++ b/src/main/java/org/casbin/config/UserSecurityConfig.java @@ -0,0 +1,31 @@ +// Copyright 2024 The Casdoor Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package org.casbin.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; + +@Configuration +@EnableWebSecurity +@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) +public class UserSecurityConfig extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + http.authorizeRequests(). + antMatchers("/**").permitAll(); + } +} diff --git a/src/main/java/org/casbin/controller/AdapterController.java b/src/main/java/org/casbin/controller/AdapterController.java new file mode 100644 index 0000000..857e76e --- /dev/null +++ b/src/main/java/org/casbin/controller/AdapterController.java @@ -0,0 +1,85 @@ +// Copyright 2024 The Casdoor Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package org.casbin.controller; + +import org.casbin.adapter.JDBCAdapter; +import org.casbin.api.CommonResult; +import org.casbin.jcasbin.main.Enforcer; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpSession; +import java.util.ArrayList; + + +@RestController +@RequestMapping("/casbin") +public class AdapterController { + @Value(value = "${mysql.url}") + private String url; + @Value(value = "${mysql.username}") + private String username; + @Value(value = "${mysql.password}") + private String password; + @Value(value = "${mysql.driver}") + private String driver; + + @RequestMapping("/InitDataAndLogin") + public CommonResult InitDataAndLogin(Authentication authentication, HttpSession session) throws Exception { + + init(); + SecurityContext context = SecurityContextHolder.getContext(); + + SimpleGrantedAuthority authority = new SimpleGrantedAuthority("readData1"); + ArrayList arrayList = new ArrayList<>(); + arrayList.add(authority); + UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("alice", "pass", arrayList); + + context.setAuthentication(token); + return CommonResult.success("login success, init data"); + } + + @RequestMapping("/canReadData1") + @PreAuthorize("hasAuthority('readData1')") + public CommonResult canReadData1() { + return CommonResult.success("you can read data1"); + } + + @RequestMapping("/canNotReadData2") + @PreAuthorize("hasAuthority('readData2')") + public CommonResult canNotReadData() { + return CommonResult.success("you can read data2"); + } + + /** + * use init once to load model data into mysql database + * then you can dismiss it + * + * @throws Exception + */ + public void init() throws Exception { + Enforcer e = new Enforcer("examples/rbac_model.conf", "examples/rbac_policy.csv"); + JDBCAdapter adapter = new JDBCAdapter(driver, url, username, password); + adapter.savePolicy(e.getModel()); + } + +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000..65dc640 --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,12 @@ +server: + port: 8090 +spring: + security: + user: + name: user + password: pass +mysql: + url: jdbc:mysql://localhost:3306/casbin?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=PRC&characterEncoding=UTF8 + username: root + password: 38941abc + driver: com.mysql.cj.jdbc.Driver \ No newline at end of file diff --git a/src/test/java/org/casbin/JdbcTest.java b/src/test/java/org/casbin/JdbcTest.java new file mode 100644 index 0000000..c275cd0 --- /dev/null +++ b/src/test/java/org/casbin/JdbcTest.java @@ -0,0 +1,56 @@ +// Copyright 2024 The Casdoor Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package org.casbin; + +import org.casbin.adapter.JDBCAdapter; +import org.casbin.jcasbin.main.Enforcer; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.context.junit4.SpringRunner; + +@SpringBootTest +public class JdbcTest { + + private String url="jdbc:mysql://localhost:3306/casbin?useSSL=false&allowPublicKeyRetrieval=true"; + private String username="root"; + private String password="casbin_test"; + private String driver="com.mysql.jdbc.Driver"; + + @Test + public void TestJdbc() throws Exception { + //save policy to database + Enforcer e = new Enforcer("examples/rbac_model.conf", "examples/rbac_policy.csv"); + JDBCAdapter adapter = new JDBCAdapter(driver, url, username, password); + adapter.savePolicy(e.getModel()); + + //read policy to database + e.clearPolicy(); + adapter.loadPolicy(e.getModel()); + + Enforcer newOne=new Enforcer("examples/rbac_model.conf",e.getAdapter()); + System.out.println(newOne.getPolicy()); + + } + + @Test + public void TestModelAuth(){ + Enforcer e = new Enforcer("examples/rbac_model.conf", "examples/rbac_policy.csv"); + boolean enforce = e.enforce("alice", "data1", "read"); + System.out.println(enforce); + } +} diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml new file mode 100644 index 0000000..5970557 --- /dev/null +++ b/src/test/resources/application.yml @@ -0,0 +1,7 @@ +server: + port: 8090 +spring: + security: + user: + name: user + password: pass