Compare commits

...

198 Commits

Author SHA1 Message Date
0de8e247cb Update dependency org.apache.commons:commons-lang3 to v3.18.0 2025-09-11 22:01:52 +00:00
8d4f331880 Update dependency gradle to v8.14.3 2025-09-11 21:03:00 +00:00
7f1b77ce19 Update dependency dnsjava:dnsjava to v3.6.3 2025-09-11 21:02:17 +00:00
d4080bb2e5 Update dependency commons-io:commons-io to v2.20.0 2025-09-11 20:01:55 +00:00
0106364680 Update dependency commons-codec:commons-codec to v1.19.0 2025-09-11 20:01:50 +00:00
b1ef1b9aa1 Update dependency com.mchange:c3p0 to v0.11.2 2025-09-11 19:02:25 +00:00
84b4c9c3ed Update dependency com.github.ben-manes:gradle-versions-plugin to v0.52.0 2025-09-11 19:02:18 +00:00
dedce04961 Update dependency org.postgresql:postgresql to v42.7.7 2025-09-11 18:01:43 +00:00
a4e99fcc79 Update dependency org.xerial:sqlite-jdbc to v3.50.3.0 2025-09-11 17:02:23 +00:00
d568678df5 Update dependency com.twilio.sdk:twilio to v10.9.2 2025-09-11 17:02:19 +00:00
31210f4e71 Update dependency org.apache.directory.api:api-all to v2.1.7 2025-09-11 17:02:08 +00:00
df70b65ac3 Update dependency org.mariadb.jdbc:mariadb-java-client to v3.5.6 2025-09-11 16:01:44 +00:00
949bfc38f8 Merge pull request 'Update dependency com.mchange:c3p0 to v0.10.1' (#51) from renovate/com.mchange-c3p0-0.x into master
Reviewed-on: #51
2024-05-09 12:15:33 +00:00
c5e5f643a2 Merge pull request 'Update dependency com.googlecode.libphonenumber:libphonenumber to v8.13.36' (#50) from renovate/com.googlecode.libphonenumber-libphonenumber-8.x into master
Reviewed-on: #50
2024-05-09 12:15:23 +00:00
5820156512 Merge pull request 'Update dependency commons-codec:commons-codec to v1.17.0' (#49) from renovate/commons-codec-commons-codec-1.x into master
Reviewed-on: #49
2024-05-09 12:15:15 +00:00
7cc4be7cd6 Update dependency com.mchange:c3p0 to v0.10.1 2024-05-07 15:01:15 +00:00
5e31ff9b69 Update dependency com.googlecode.libphonenumber:libphonenumber to v8.13.36 2024-05-02 09:01:21 +00:00
d910472db5 Update dependency commons-codec:commons-codec to v1.17.0 2024-04-27 14:01:32 +00:00
ca56afad5d Merge pull request 'Update dependency io.undertow:undertow-core to v2.3.13.Final' (#48) from renovate/io.undertow-undertow-core-2.x into master
Reviewed-on: #48
2024-04-23 11:58:50 +00:00
541aeee3c2 Merge pull request 'Update dependency com.twilio.sdk:twilio to v10.1.5' (#47) from renovate/com.twilio.sdk-twilio-10.x into master
Reviewed-on: #47
2024-04-23 11:58:42 +00:00
19257a0290 Merge pull request 'Update dependency com.googlecode.libphonenumber:libphonenumber to v8.13.35' (#46) from renovate/com.googlecode.libphonenumber-libphonenumber-8.x into master
Reviewed-on: #46
2024-04-23 11:58:32 +00:00
8d91e7d5a3 Merge pull request 'Update dependency commons-cli:commons-cli to v1.7.0' (#45) from renovate/commons-cli-commons-cli-1.x into master
Reviewed-on: #45
2024-04-23 11:58:23 +00:00
c0671acf60 Merge pull request 'Update dependency org.xerial:sqlite-jdbc to v3.45.3.0' (#44) from renovate/org.xerial-sqlite-jdbc-3.x into master
Reviewed-on: #44
2024-04-23 11:58:14 +00:00
0b8300c774 Merge pull request 'Update dependency org.slf4j:slf4j-simple to v2.0.13' (#43) from renovate/slf4j-monorepo into master
Reviewed-on: #43
2024-04-23 11:58:05 +00:00
cac55d5f06 Merge pull request 'Update dependency commons-io:commons-io to v2.16.1' (#42) from renovate/commons-io-commons-io-2.x into master
Reviewed-on: #42
2024-04-23 11:57:55 +00:00
aa3aadbb53 Merge pull request 'Update dependency com.kohlschutter.junixsocket:junixsocket-core to v2.9.1' (#41) from renovate/com.kohlschutter.junixsocket-junixsocket-core-2.x into master
Reviewed-on: #41
2024-04-23 11:57:45 +00:00
da7fa9edd6 Update dependency io.undertow:undertow-core to v2.3.13.Final 2024-04-19 13:01:54 +00:00
a1a21e8d60 Update dependency com.twilio.sdk:twilio to v10.1.5 2024-04-18 15:01:47 +00:00
dc2932cf51 Update dependency com.googlecode.libphonenumber:libphonenumber to v8.13.35 2024-04-18 10:01:42 +00:00
bb84e32719 Update dependency commons-cli:commons-cli to v1.7.0 2024-04-18 02:01:51 +00:00
12aa40b160 Update dependency org.xerial:sqlite-jdbc to v3.45.3.0 2024-04-16 04:01:50 +00:00
9d4fddcb2d Update dependency org.slf4j:slf4j-simple to v2.0.13 2024-04-12 15:01:46 +00:00
4654cff158 Update dependency commons-io:commons-io to v2.16.1 2024-04-08 18:01:40 +00:00
b1888fed96 Update dependency com.kohlschutter.junixsocket:junixsocket-core to v2.9.1 2024-04-05 16:01:34 +00:00
d7e8b3d62a Java 21 2024-04-02 18:02:39 +02:00
56bfdda18c Update Java 2024-04-02 17:57:00 +02:00
fb3debfb49 Replace apk with apt 2024-04-02 17:52:14 +02:00
49f812f867 Bump Java to 22 2024-04-02 17:45:01 +02:00
de0a3152c3 Bump JRE to 21 2024-04-02 16:08:59 +02:00
cc5d047c3f Merge pull request 'Update dependency com.googlecode.libphonenumber:libphonenumber to v8.13.34' (#40) from renovate/com.googlecode.libphonenumber-libphonenumber-8.x into master
Reviewed-on: #40
2024-04-02 13:54:24 +00:00
350776df17 Merge pull request 'Update dependency com.twilio.sdk:twilio to v10.1.3' (#39) from renovate/com.twilio.sdk-twilio-10.x into master
Reviewed-on: #39
2024-04-02 13:54:13 +00:00
7b6560e9c8 Merge pull request 'Update dependency commons-io:commons-io to v2.16.0' (#38) from renovate/commons-io-commons-io-2.x into master
Reviewed-on: #38
2024-04-02 13:54:00 +00:00
4fd4fdac60 Updates to build sucessfully - breaking changes in Firebase and SendGrid 2024-04-02 15:42:15 +02:00
dfad9d9ce8 Update dependency com.googlecode.libphonenumber:libphonenumber to v8.13.34 2024-04-02 13:01:23 +00:00
d2fc4e3bef Update dependency com.twilio.sdk:twilio to v10.1.3 2024-04-01 11:01:26 +00:00
4416c17216 Update dependency commons-io:commons-io to v2.16.0 2024-03-28 23:01:26 +00:00
f54ed462b1 Update shadowJar to new config format 2024-03-27 16:28:52 +01:00
7498dcf122 Merge pull request 'Update dependency org.yaml:snakeyaml to v2' (#37) from renovate/org.yaml-snakeyaml-2.x into master
Reviewed-on: #37
2024-03-27 14:28:16 +00:00
7e5665a56f Merge pull request 'Update dependency org.slf4j:slf4j-simple to v2' (#36) from renovate/major-slf4j-monorepo into master
Reviewed-on: #36
2024-03-27 14:28:05 +00:00
f633e9d256 Merge pull request 'Update dependency org.mariadb.jdbc:mariadb-java-client to v3' (#35) from renovate/org.mariadb.jdbc-mariadb-java-client-3.x into master
Reviewed-on: #35
2024-03-27 14:27:56 +00:00
bdfe4a00a9 Merge pull request 'Update dependency org.apache.directory.api:api-all to v2' (#34) from renovate/org.apache.directory.api-api-all-2.x into master
Reviewed-on: #34
2024-03-27 14:27:48 +00:00
50d4f0fa30 Update dependency org.yaml:snakeyaml to v2 2024-03-27 14:01:31 +00:00
bb190230b2 Update dependency org.slf4j:slf4j-simple to v2 2024-03-27 14:01:29 +00:00
c9bafd9af2 Update ma1sd to mxids 2024-03-27 14:10:04 +01:00
3c9c8ca1b5 Update dependency org.mariadb.jdbc:mariadb-java-client to v3 2024-03-27 13:01:32 +00:00
dacf96140c Update dependency org.apache.directory.api:api-all to v2 2024-03-27 13:01:30 +00:00
1d228d75e6 Update .github/workflows/codeql-analysis.yml.disabled 2024-03-27 12:39:33 +00:00
62530eda08 Update .github/workflows/codeql-analysis.disable 2024-03-27 12:39:21 +00:00
15c3e6b425 Update .github/workflows/codeql-analysis.disable.yml
Some checks failed
CodeQL / Analyze (java) (push) Failing after 31s
2024-03-27 12:38:58 +00:00
945a2dbdf5 Merge pull request 'Update dependency gradle to v8' (#33) from renovate/gradle-8.x into master
Some checks failed
CodeQL / Analyze (java) (push) Failing after 28s
Reviewed-on: #33
2024-03-27 12:33:46 +00:00
d5cebf103d Merge pull request 'Update dependency dnsjava:dnsjava to v3' (#32) from renovate/dnsjava-dnsjava-3.x into master
Some checks failed
CodeQL / Analyze (java) (push) Has been cancelled
Reviewed-on: #32
2024-03-27 12:33:26 +00:00
de14f80a40 Merge pull request 'Update dependency com.unboundid:unboundid-ldapsdk to v7' (#31) from renovate/com.unboundid-unboundid-ldapsdk-7.x into master
Some checks failed
CodeQL / Analyze (java) (push) Has been cancelled
Reviewed-on: #31
2024-03-27 12:33:12 +00:00
7ed6db537d Update codeql api version
Some checks failed
CodeQL / Analyze (java) (push) Failing after 1m33s
2024-03-27 12:28:02 +00:00
9d8d88527c Update .github/workflows/codeql-analysis.yml
Some checks failed
CodeQL / Analyze (java) (push) Failing after 33s
2024-03-27 12:26:01 +00:00
a1c82878f2 Update dependency gradle to v8
Some checks failed
CodeQL / Analyze (java) (pull_request) Failing after 21s
2024-03-27 12:02:36 +00:00
ab95c8ee32 Update dependency dnsjava:dnsjava to v3
Some checks failed
CodeQL / Analyze (java) (pull_request) Failing after 20s
2024-03-27 12:02:04 +00:00
e50a49b198 Update dependency com.unboundid:unboundid-ldapsdk to v7
Some checks failed
CodeQL / Analyze (java) (pull_request) Failing after 20s
2024-03-27 12:02:04 +00:00
4492888557 Merge pull request 'Update dependency com.twilio.sdk:twilio to v10' (#30) from renovate/com.twilio.sdk-twilio-10.x into master
Reviewed-on: #30
2024-03-27 11:52:25 +00:00
ed5407fc42 Merge pull request 'Update dependency com.sendgrid:sendgrid-java to v4' (#29) from renovate/com.sendgrid-sendgrid-java-4.x into master
Reviewed-on: #29
2024-03-27 11:52:16 +00:00
a2911f2ace Merge pull request 'Update dependency com.j256.ormlite:ormlite-jdbc to v6' (#28) from renovate/com.j256.ormlite-ormlite-jdbc-6.x into master
Reviewed-on: #28
2024-03-27 11:52:03 +00:00
e99578d168 Merge pull request 'Update dependency com.icegreen:greenmail to v2' (#27) from renovate/com.icegreen-greenmail-2.x into master
Reviewed-on: #27
2024-03-27 11:51:53 +00:00
d471ea71d7 Merge pull request 'Update dependency com.google.firebase:firebase-admin to v9' (#26) from renovate/com.google.firebase-firebase-admin-9.x into master
Reviewed-on: #26
2024-03-27 11:51:42 +00:00
56115df282 Merge pull request 'Update dependency com.github.tomakehurst:wiremock to v3' (#25) from renovate/com.github.tomakehurst-wiremock-3.x into master
Reviewed-on: #25
2024-03-27 11:51:27 +00:00
2e1194d216 Merge pull request 'Update dependency org.yaml:snakeyaml to v1.33' (#24) from renovate/org.yaml-snakeyaml-1.x into master
Reviewed-on: #24
2024-03-27 11:51:18 +00:00
ee001d543c Merge pull request 'Update dependency org.apache.commons:commons-lang3 to v3.14.0' (#21) from renovate/org.apache.commons-commons-lang3-3.x into master
Reviewed-on: #21
2024-03-27 11:51:06 +00:00
a826eb319e Merge pull request 'Update dependency commons-codec:commons-codec to v1.16.1' (#17) from renovate/commons-codec-commons-codec-1.x into master
Reviewed-on: #17
2024-03-27 11:50:56 +00:00
6b557e9954 Update dependency com.twilio.sdk:twilio to v10 2024-03-27 10:02:03 +00:00
041a4d1b73 Update dependency com.sendgrid:sendgrid-java to v4 2024-03-27 09:02:08 +00:00
c1a1741bfb Update dependency com.j256.ormlite:ormlite-jdbc to v6 2024-03-27 09:02:07 +00:00
6e1058c484 Update dependency com.icegreen:greenmail to v2 2024-03-27 08:02:04 +00:00
5f6fb38485 Update dependency com.google.firebase:firebase-admin to v9 2024-03-27 08:02:03 +00:00
c9046ffbd0 Update dependency com.github.tomakehurst:wiremock to v3 2024-03-27 07:02:17 +00:00
cf97c5a88f Update dependency org.yaml:snakeyaml to v1.33 2024-03-27 07:02:15 +00:00
3d1a8c4495 Update dependency org.apache.commons:commons-lang3 to v3.14.0 2024-03-27 07:02:14 +00:00
46dc07ef07 Update dependency commons-codec:commons-codec to v1.16.1 2024-03-27 07:02:13 +00:00
d9ab2b8739 Merge pull request 'Update dependency org.xerial:sqlite-jdbc to v3.45.2.0' (#23) from renovate/org.xerial-sqlite-jdbc-3.x into master
Reviewed-on: #23
2024-03-27 06:10:34 +00:00
14032aaf50 Merge pull request 'Update dependency org.postgresql:postgresql to v42.7.3' (#22) from renovate/org.postgresql-postgresql-42.x into master
Reviewed-on: #22
2024-03-27 06:10:23 +00:00
73051c3d00 Merge pull request 'Update dependency io.undertow:undertow-core to v2.3.12.Final' (#20) from renovate/io.undertow-undertow-core-2.x into master
Reviewed-on: #20
2024-03-27 06:10:04 +00:00
a3c8c4a8cf Merge pull request 'Update dependency gradle to v7.6.4' (#19) from renovate/gradle-7.x into master
Reviewed-on: #19
2024-03-27 06:09:47 +00:00
453751a39e Merge pull request 'Update dependency commons-io:commons-io to v2.15.1' (#18) from renovate/commons-io-commons-io-2.x into master
Reviewed-on: #18
2024-03-27 06:09:06 +00:00
ad23e91ece Merge pull request 'Update dependency commons-cli:commons-cli to v1.6.0' (#16) from renovate/commons-cli-commons-cli-1.x into master
Reviewed-on: #16
2024-03-27 06:08:42 +00:00
baabdfc2ef Merge pull request 'Update dependency com.twilio.sdk:twilio to v7.55.3' (#15) from renovate/com.twilio.sdk-twilio-7.x into master
Reviewed-on: #15
2024-03-27 06:08:20 +00:00
263e3260c9 Merge pull request 'Update dependency com.squareup.okhttp3:okhttp to v4.12.0' (#14) from renovate/okhttp-monorepo into master
Reviewed-on: #14
2024-03-27 06:08:10 +00:00
a281bdbf77 Update dependency org.xerial:sqlite-jdbc to v3.45.2.0 2024-03-26 22:02:15 +00:00
338e7f4aa2 Update dependency org.postgresql:postgresql to v42.7.3 2024-03-26 22:02:13 +00:00
a9212ce73a Update dependency io.undertow:undertow-core to v2.3.12.Final 2024-03-26 21:02:09 +00:00
f2e1fcb25f Update dependency gradle to v7.6.4 2024-03-26 20:02:39 +00:00
f5630bc4d9 Update dependency commons-io:commons-io to v2.15.1 2024-03-26 20:02:09 +00:00
dbccaecfd6 Update dependency commons-cli:commons-cli to v1.6.0 2024-03-26 19:02:15 +00:00
de0842d3a8 Update dependency com.twilio.sdk:twilio to v7.55.3 2024-03-26 18:02:10 +00:00
05424f14b8 Update dependency com.squareup.okhttp3:okhttp to v4.12.0 2024-03-26 18:02:08 +00:00
2587e6be7c Merge pull request 'Update dependency com.mchange:c3p0 to v0.10.0' (#13) from renovate/com.mchange-c3p0-0.x into master
Reviewed-on: #13
2024-03-26 17:39:03 +00:00
20062fe5bc Merge pull request 'Update dependency com.kohlschutter.junixsocket:junixsocket-core to v2.9.0' (#12) from renovate/com.kohlschutter.junixsocket-junixsocket-core-2.x into master
Reviewed-on: #12
2024-03-26 17:38:51 +00:00
0ec6462ff4 Update dependency com.mchange:c3p0 to v0.10.0 2024-03-26 17:02:16 +00:00
567c7bb972 Update dependency com.kohlschutter.junixsocket:junixsocket-core to v2.9.0 2024-03-26 17:02:14 +00:00
1c6322f1c2 Merge pull request 'Update dependency com.j256.ormlite:ormlite-jdbc to v5.7' (#11) from renovate/com.j256.ormlite-ormlite-jdbc-5.x into master
Reviewed-on: #11
2024-03-26 16:05:03 +00:00
603cdd1340 Merge pull request 'Update dependency com.icegreen:greenmail to v1.6.15' (#10) from renovate/com.icegreen-greenmail-1.x into master
Reviewed-on: #10
2024-03-26 16:04:50 +00:00
a6164379c5 Update dependency com.j256.ormlite:ormlite-jdbc to v5.7 2024-03-26 16:02:05 +00:00
fc9d6b3a36 Update dependency com.icegreen:greenmail to v1.6.15 2024-03-26 16:02:03 +00:00
8b14664dc7 Merge pull request 'Update dependency com.googlecode.libphonenumber:libphonenumber to v8.13.33' (#9) from renovate/com.googlecode.libphonenumber-libphonenumber-8.x into master
Reviewed-on: #9
2024-03-26 15:18:14 +00:00
f0922d67df Merge pull request 'Update dependency com.google.firebase:firebase-admin to v5.11.0' (#8) from renovate/com.google.firebase-firebase-admin-5.x into master
Reviewed-on: #8
2024-03-26 15:18:02 +00:00
6e05b51fdc Update dependency com.googlecode.libphonenumber:libphonenumber to v8.13.33 2024-03-26 15:02:08 +00:00
0999ef6309 Update dependency com.google.firebase:firebase-admin to v5.11.0 2024-03-26 15:02:06 +00:00
cae00f4606 Merge pull request 'Update dependency com.github.ben-manes:gradle-versions-plugin to v0.51.0' (#7) from renovate/com.github.ben-manes-gradle-versions-plugin-0.x into master
Reviewed-on: #7
2024-03-26 14:15:58 +00:00
941ae697e7 Merge pull request 'Update openjdk Docker tag to v11.0.16-jre-slim' (#6) from renovate/openjdk-11.x into master
Reviewed-on: #6
2024-03-26 14:15:43 +00:00
fbf1ea8f5b Merge pull request 'Update dependency org.slf4j:slf4j-simple to v1.7.36' (#5) from renovate/slf4j-monorepo into master
Reviewed-on: #5
2024-03-26 14:15:25 +00:00
4eef3ac40d Merge pull request 'Update dependency org.mariadb.jdbc:mariadb-java-client to v2.7.12' (#4) from renovate/org.mariadb.jdbc-mariadb-java-client-2.x into master
Reviewed-on: #4
2024-03-26 14:15:06 +00:00
e6c90aa00f Update dependency com.github.ben-manes:gradle-versions-plugin to v0.51.0 2024-03-26 14:02:17 +00:00
ca306a3212 Update openjdk Docker tag to v11.0.16-jre-slim 2024-03-26 14:02:15 +00:00
e8864afe02 Update dependency org.slf4j:slf4j-simple to v1.7.36 2024-03-26 13:02:02 +00:00
f09ede8780 Update dependency org.mariadb.jdbc:mariadb-java-client to v2.7.12 2024-03-26 13:02:01 +00:00
98146d3468 Merge pull request 'Update dependency org.apache.httpcomponents:httpclient to v4.5.14' (#3) from renovate/org.apache.httpcomponents-httpclient-4.x into master
Reviewed-on: #3
2024-03-26 12:25:59 +00:00
5f64b72a91 Merge pull request 'Update dependency com.unboundid:unboundid-ldapsdk to v4.0.14' (#2) from renovate/com.unboundid-unboundid-ldapsdk-4.x into master
Reviewed-on: #2
2024-03-26 12:25:43 +00:00
acb2b5a37d Update ma1sd to mxids 2024-03-26 13:21:48 +01:00
9f9c48aaa7 Update dependency org.apache.httpcomponents:httpclient to v4.5.14 2024-03-26 12:21:43 +00:00
eb6e541e15 Update dependency com.unboundid:unboundid-ldapsdk to v4.0.14 2024-03-26 12:21:41 +00:00
e45ac5a78a Merge pull request 'Add renovate.json' (#1) from tomas.kracmar-add-renovatebot-support into master
Reviewed-on: #1
2024-03-14 09:06:29 +00:00
aebcb7b5e1 Add renovate.json 2024-03-14 09:06:12 +00:00
c45f95ce3f Merge pull request 'Upgrade to V3 API' (#1) from ext/v3_update into master
Reviewed-on: cqrenet/ma1sd#1
2024-03-11 08:51:06 +00:00
640fa8e9f1 Upgrade to V3 API 2024-03-11 09:50:09 +01:00
0f3c37bf6a Get access_token from header correctly 2024-03-05 17:50:29 +01:00
Anatoliy Sablin
ae5864cd91 Bump dependencies. 2021-04-16 21:48:19 +03:00
Anatoliy Sablin
e456724caf Bump gradle to 7.0. Replace jcenter with mavenCentral and gradle plugin portal. 2021-04-16 20:58:32 +03:00
Anatoliy Sablin
ed9dcc4061 Respond with application/json for the register submitToken. 2021-02-04 21:10:25 +03:00
Anatoliy Sablin
ea8e386939 Add internal API to manually invoke invitation manager. 2021-01-25 22:45:18 +03:00
Anatoliy Sablin
e0ec887118 Add config print full display name of the invited person. 2021-01-17 20:06:09 +03:00
Anatoliy Sablin
a71d32ba77 Add config option to specify period dimension of the invitation scheduler. 2021-01-13 22:09:30 +03:00
Anatoliy Sablin
a0f6fe9b0d Add forgotten M_TERMS_NOT_SIGNED error message. 2021-01-13 21:41:21 +03:00
ma1uta
c25647156a Merge pull request #77 from mrjohnson22/master
#76 Set a message for error responses
2020-12-20 11:14:20 +00:00
Xavier Johnson
e7c4c12a98 #76 Set a message for error responses
Without one, clients might treat errors as generic failures instead of
handling them in a manner appropriate for their error code
2020-12-18 23:00:06 -05:00
Anatoliy Sablin
90b2b5301c #68 Mark thusted_third_party_id_servers synapse parameter as deprecated. 2020-12-07 20:39:47 +03:00
Anatoliy Sablin
0d93a26e6d #65 Encode query parameters in the validation link. 2020-12-07 20:32:59 +03:00
ma1uta
c29fc0f0eb Merge pull request #71 from q-wertz/master
Force MatrixID to be lowercase
2020-11-29 08:22:45 +00:00
Clemens Sonnleitner
e421c851c9 Force MatrixID to be lowercase 2020-11-27 13:08:45 +01:00
ma1uta
5b2b45233a Merge pull request #67 from Higgs1/master
Add support for unix sockets.
2020-11-05 05:56:26 +00:00
Lexxy Fox
888f7a4209 Added support for unix sockets. 2020-11-04 08:21:35 -08:00
Anatoliy Sablin
0c301a49c7 Change column type to text for postgresql. 2020-10-26 23:26:15 +03:00
ma1uta
1fda2dd3b7 Create codeql-analysis.yml 2020-10-01 14:51:31 +03:00
ma1uta
c4a20efe5e Merge pull request #58 from nE0sIghT/wip/multidomain
Support for Active Directory multidomain forest
2020-09-19 13:14:19 +00:00
ma1uta
fc45f1b090 Merge pull request #55 from mattcen/docker-build-cleanup
Docker build cleanup
2020-09-19 13:13:16 +00:00
Matt Cengia
1480507d76 Only build .jar on current build platform
Because the .jar is platform-independent, we don't need to build it for every architecture.
2020-09-18 12:45:33 +10:00
Matt Cengia
a1ab1e8e0a Tidy Gradle dockerBuildX target
Build linux/amd64, linux/arm64, and linux/arm/v7 (arm 32-bit) targets
all at once.
This still requires Gradle and JDK on the local machine (i.e. outside of
the Docker build container), because it seems pointless to build the
.jar 3 times, once for each architecture, but tags all the images with
the same tag, rather than using a tag for each architecture. This allows
clients to all use the same image tag, but pull down the architecture
that's right for them.

I'd like to have it build the .jar file in a container (so the host
doesn't need JDK and Gradle), but couldn't think how to do that
efficiently (i.e. only once), with this approach.
2020-09-17 10:54:29 +10:00
Matt Cengia
1e5033b461 Don't require Gradle to build Docker image
Update Dockerfile to use a multi-stage build and run Gradle *within* the
builder container, rather than on the host, so the host needn't have
Gradle or JDK installed, then copy the build .jar from the builder
container into the final container.

Update build.gradle's dockerBuild target to not build the jar, but
instead do it within the Docker build process. This results in some
double-handling because it requires Gradle and JDK both on the host
system *and* within the container, and runs two instances of Gradle
during the build, but is added for backwards compatibility. The better
approach (rather than running `./gradlew dockerBuild`) is to manually
run `docker build -t ma1ua/ma1sd .`.

I've tested this process on both a Debian amd64 system and a Raspberry
Pi 3 running Raspbian, and it seems to work (though the RPi is pretty
slow).
2020-09-17 10:54:29 +10:00
Yuri Konotopov
7323851c6e Support for Active Directory multidomain forest
In AD forest samAccountName (or uid) may not be unique in the
entire forest and userPrincipalName contains "@" symbol
disallowed in Matrix User Identifiers.

This commit reflects changes in ldap_auth_provider that adds
mxid generation logic for Active Directory.

Signed-off-by: Yuri Konotopov <ykonotopov@gnome.org>
2020-08-28 15:33:10 +04:00
Anatoliy Sablin
08db73e55b Escape special characters in the LDAP query string. 2020-08-02 16:05:54 +03:00
Anatoliy Sablin
9fba20475b fix #49. 2020-06-23 00:18:27 +03:00
ma1uta
9af5fce014 Merge pull request #50 from lub/patch-1
remove warning about matrix-synapse-ldap3
2020-06-22 19:54:51 +00:00
ma1uta
9843e14c1a Merge pull request #38 from NullIsNot0/NullIsNot0-make-emails-lowercase
Make all 3PID address lowercase to avoid duplicates
2020-06-22 19:51:42 +00:00
lub
60e6f1e23c fix typo
on -> one
2020-06-05 13:23:37 +02:00
lub
6cdbcc69c7 remove warning about matrix-synapse-ldap3
I don't think it's a fair assessment that the project is unmaintaned. When you look at the issues and PRs there still is active development happening.
2020-06-05 13:22:49 +02:00
Anatoliy Sablin
ed7c714738 Fix #41. 2020-05-31 22:56:01 +03:00
Anatoliy Sablin
a9d783192b Add multiple-platform builds. Add experimental arm64 build. 2020-05-31 22:10:32 +03:00
ma1uta
2bb5a734d1 Merge pull request #45 from teutat3s/fix_directory_lookups
Avoid including bridged user in directory lookups
2020-05-19 19:32:31 +00:00
teutat3s
9aa5c4cca9 Avoid including bridged user in directory lookups 2020-05-19 13:04:22 +02:00
Anatoliy Sablin
9c4faab5d8 Add option to log all requests and responses. 2020-05-06 23:46:34 +03:00
Anatoliy Sablin
53c4ffdc4e Add pooling database connection for postgresql. 2020-05-06 20:55:14 +03:00
Anatoliy Sablin
e4144e923a Add error logs. 2020-05-06 19:47:13 +03:00
Anatoliy Sablin
791361c10d Add the migration to fix column types in the postgresql. 2020-05-06 19:39:33 +03:00
NullIsNot0
7c94bd4744 Make all 3PID address lowercase to avoid duplicates
These changes complement #11 where locally saved e-mail address can be "name.surname@example.com", but e-mail address in LDAP can be "Name.Surname@example.com". They are treated as two different e-mail addresses and user gets 2 invitation notification e-mails. We change ThreePid model's address property to convert all info to lowercase and [be915ae](be915aed94) can do it's job better.
The downside of this is that all medium addresses get converted to lowercase, not only e-mails. For now I can't think of any examples where medium values need to stay case sensitive.
2020-05-06 07:41:44 +03:00
Anatoliy Sablin
4b5eecd7e7 Enable v2 by default because Riot require v2 api. 2020-04-21 23:27:20 +03:00
Anatoliy Sablin
a6968fb7e9 Fix #27. 2020-04-07 22:46:14 +03:00
Anatoliy Sablin
d4853b1154 Add config for hostname. 2020-04-07 22:46:14 +03:00
ma1uta
89df4b2425 Merge pull request #33 from aaronraimist/patch-1
ma1sd implements r0.3.0 of the identity server API
2020-04-05 10:20:42 +00:00
Aaron Raimist
0f89121b98 ma1sd implements r0.3.0 of the identity server API 2020-04-04 17:16:25 -05:00
Anatoliy Sablin
8a40ca185b Fix #22. 2020-03-22 12:17:33 +03:00
Anatoliy Sablin
5baeb42623 Fix #29. 2020-03-22 12:12:47 +03:00
Anatoliy Sablin
072e5f66cb #26 Use empty pepper. 2020-02-19 23:35:59 +03:00
Anatoliy Sablin
b2f41d689b #26 fix. 2020-02-19 00:36:05 +03:00
Anatoly Sablin
9b4aff58c7 Add migration documentation. 2020-01-30 23:17:01 +03:00
Anatoly Sablin
a20e41574d Update docs. Add a new options and configuration. 2020-01-28 23:20:29 +03:00
Anatoly Sablin
72977d65ae Workaround for postgresql. 2020-01-28 23:18:39 +03:00
Anatoly Sablin
7555fff1a5 Add the postgresql backend for internal storage. 2020-01-28 22:15:26 +03:00
Anatoly Sablin
aed12e5536 Add the --dump-and-exit option to exit after printing the full configuration. 2020-01-28 01:02:43 +03:00
Anatoly Sablin
75efd9921d Improve logging configuration. Introduce the root and the app log levels. 2020-01-28 00:55:39 +03:00
Anatoly Sablin
9219bd4723 Add logging configuration. Add --dump option to just print the full configuration. 2020-01-25 14:57:22 +03:00
Anatoly Sablin
73526be2ac Add configuration to use the legacy query for old synapse to get room names. 2020-01-25 14:04:40 +03:00
ma1uta
b827efca2c Merge pull request #13 from NullIsNot0/fix-room-names-patch
Fix room name retrieval after Synapse dropped table room_names
2020-01-25 10:50:55 +00:00
NullIsNot0
6b7a4c8a23 Fix room name retrieval after Synapse dropped table room_names
Recently Synapse dropped unused (by Synapse itself) table "room_names" which brakes room name retrieval for ma1sd. There is a table "room_stats_state" from which we can retrieve room name by it's ID. Note that people to people conversations do not contain room names, because they are generated on-the-fly by setting other participants names separated by word "and". That's why this query will only get names for rooms where room names are set during creation process (or changed later) and are the same for all participants.
Link to Synapse code where it drops "room_names" table: https://github.com/matrix-org/synapse/blob/master/synapse/storage/data_stores/main/schema/delta/56/drop_unused_event_tables.sql#L17
2020-01-10 18:23:29 +02:00
Anatoly Sablin
47f6239268 Add equals and hashCode methods for the MemoryThreePid. 2020-01-09 22:28:44 +03:00
ma1uta
0d6f65b469 Merge pull request #11 from NullIsNot0/master
Load DNS overwrite config on startup and remove duplicates from identity store before email notifications
2020-01-09 19:25:13 +00:00
Edgars Voroboks
be915aed94 Remove duplicates from identity store before email notifications
I use LDAP for user store. I have set up "mail" and "otherMailbox" as threepid email attributes. When people get invited to rooms, they receive 2 (sometimes 3) invitation e-mails if they have the same e-mail address in LDAP "mail" and "otherMailbox" fields. I think it's a good idea to check identity store for duplicates before sending invitation e-mails.
2020-01-09 20:14:56 +02:00
NullIsNot0
ce938bb4a5 Load DNS overwrite config on startup
I recently noticed that DNS overwrite does not happen. There are messages in logs: "No DNS overwrite for <REDACTED>", but I definitely have configured DNS overwrithng. I think it's because DNS overwriting config is not loaded when ma1sd starts up.
Documented here: https://github.com/ma1uta/ma1sd/blob/master/docs/features/authentication.md#dns-overwrite and here: https://github.com/ma1uta/ma1sd/blob/master/docs/features/directory.md#dns-overwrite
2020-01-07 22:24:26 +02:00
Anatoly Sablin
15db563e8d Add documentation. 2019-12-26 22:49:25 +03:00
Anatoly Sablin
82a538c750 Add an option to enable/disable hash lookup via the LDAP provider. 2019-12-25 22:51:44 +03:00
Anatoly Sablin
84ca8ebbd9 Add support of the MSC2134 (Identity hash lookup) for the LDAP provider. 2019-12-25 00:13:07 +03:00
Anatoly Sablin
774ebf4fa8 Fix for #9. Proper wrap the handles with the sanitize handler. 2019-12-16 22:47:24 +03:00
Anatoly Sablin
eb1326c56a Add unique id for the accepted table.
Add a little more logs.
2019-12-10 22:29:00 +03:00
Anatoly Sablin
10cdb4360e Fix homeserver verification with wildcards certificates.
Disable v2 by default.
Add migration to fix the accepted table (due to sqlite unable to change constraint, drop table and create again).
Fix displaying the expiration period of the new token.
Remove duplicated code.
Use v1 single lookup when receive the request with `none` algorithm and the only one argument.
Hide v2 endpoint if v2 API disabled.
2019-12-10 00:10:13 +03:00
139 changed files with 2755 additions and 1326 deletions

View File

@@ -0,0 +1,71 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
name: "CodeQL"
on:
push:
branches: [master]
pull_request:
# The branches below must be a subset of the branches above
branches: [master]
schedule:
- cron: '0 3 * * 6'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
# Override automatic language detection by changing the below list
# Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python']
language: ['java']
# Learn more...
# https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection
steps:
- name: Checkout repository
uses: actions/checkout@v2
with:
# We must fetch at least the immediate parents so that if this is
# a pull request then we can checkout the head.
fetch-depth: 0
# If this run was triggered by a pull request event, then checkout
# the head of the pull request instead of the merge commit.
- run: git checkout HEAD^2
if: ${{ github.event_name == 'pull_request' }}
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v2
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1

4
.gitignore vendored
View File

@@ -7,8 +7,8 @@ out/
.idea/
# Local dev config
/ma1sd.yaml
/mxids.yaml
/application.yaml
# Local dev storage
/ma1sd.db
/mxids.db

View File

@@ -1,18 +1,36 @@
FROM openjdk:8-jre-alpine
# Use a specific version of OpenJDK based on Debian ("bullseye" in this case)
FROM --platform=$BUILDPLATFORM openjdk:21-jdk-bullseye AS builder
RUN apk update && apk add bash && rm -rf /var/lib/apk/* /var/cache/apk/*
# Replace 'apk' commands with 'apt-get' for Debian-based package management.
# Install required packages such as 'git' and 'gradle'. Remember to update and clean up properly.
RUN apt-get update && \
apt-get install -y gradle git && \
rm -rf /var/lib/apt/lists/*
VOLUME /etc/ma1sd
VOLUME /var/ma1sd
WORKDIR /mxids
COPY . .
RUN ./gradlew shadowJar
# Second stage: Setup the runtime container
FROM openjdk:21-jdk-bullseye
# Again, switch to 'apt-get' for installing 'bash'. Clean up to keep the image size down.
RUN apt-get update && \
apt-get install -y bash && \
rm -rf /var/lib/apt/lists/*
VOLUME /etc/mxids
VOLUME /var/mxids
EXPOSE 8090
ENV JAVA_OPTS=""
ENV CONF_FILE_PATH="/etc/ma1sd/ma1sd.yaml"
ENV SIGN_KEY_PATH="/var/ma1sd/sign.key"
ENV SQLITE_DATABASE_PATH="/var/ma1sd/ma1sd.db"
ENV CONF_FILE_PATH="/etc/mxids/mxids.yaml"
ENV SIGN_KEY_PATH="/var/mxids/sign.key"
ENV SQLITE_DATABASE_PATH="/var/mxids/mxids.db"
CMD [ "/start.sh" ]
# It's usually a good practice to use 'COPY' instead of 'ADD' for local files unless you need the extra capabilities of 'ADD' (like auto-extracting tar files).
COPY src/docker/start.sh /start.sh
COPY src/script/mxids /app/mxids
COPY --from=builder /mxids/build/libs/mxids.jar /app/mxids.jar
ADD src/docker/start.sh /start.sh
ADD src/script/ma1sd /app/ma1sd
ADD build/libs/ma1sd.jar /app/ma1sd.jar
CMD ["/start.sh"]

16
DockerfileX Normal file
View File

@@ -0,0 +1,16 @@
FROM --platform=$BUILDPLATFORM openjdk:11.0.16-jre-slim
VOLUME /etc/mxids
VOLUME /var/mxids
EXPOSE 8090
ENV JAVA_OPTS=""
ENV CONF_FILE_PATH="/etc/mxids/mxids.yaml"
ENV SIGN_KEY_PATH="/var/mxids/sign.key"
ENV SQLITE_DATABASE_PATH="/var/mxids/mxids.db"
CMD [ "/start.sh" ]
ADD src/docker/start.sh /start.sh
ADD src/script/mxids /app/mxids
ADD build/libs/mxids.jar /app/mxids.jar

View File

@@ -1,5 +1,5 @@
/*
* ma1sd - Matrix Identity Server Daemon
* mxids - Matrix Identity Server
* Copyright (C) 2017 Kamax Sarl
*
* https://www.kamax.io/
@@ -20,22 +20,22 @@
import java.util.regex.Pattern
apply plugin: 'java'
apply plugin: 'java-library'
apply plugin: 'application'
apply plugin: 'com.github.johnrengelman.shadow'
apply plugin: 'idea'
apply plugin: 'com.github.ben-manes.versions'
def confFileName = "ma1sd.example.yaml"
def confFileName = "mxids.example.yaml"
def distDir = "${project.buildDir}/dist"
def debBinPath = "/usr/lib/ma1sd"
def debConfPath = "/etc/ma1sd"
def debDataPath = "/var/lib/ma1sd"
def debBinPath = "/usr/lib/mxids"
def debConfPath = "/etc/mxids"
def debDataPath = "/var/lib/mxids"
def debSystemdPath = "/etc/systemd/system"
def debConfFileName = confFileName
def debStartScriptFilename = "ma1sd"
def debStartScriptFilename = "mxids"
def debBuildBasePath = "${project.buildDir}/tmp/debian"
def debBuildDebianPath = "${debBuildBasePath}/DEBIAN"
@@ -44,18 +44,18 @@ def debBuildConfPath = "${debBuildBasePath}${debConfPath}"
def debBuildDataPath = "${debBuildBasePath}${debDataPath}"
def debBuildSystemdPath = "${debBuildBasePath}${debSystemdPath}"
def dockerImageName = "ma1uta/ma1sd"
def dockerImageTag = "${dockerImageName}:${ma1sdVersion()}"
def dockerImageName = "cqrenet/mxids"
def dockerImageTag = "${dockerImageName}:${mxidsVersion()}"
group = 'io.kamax'
mainClassName = 'io.kamax.mxisd.MxisdStandaloneExec'
sourceCompatibility = '1.8'
targetCompatibility = '1.8'
sourceCompatibility = '11'
targetCompatibility = '11'
String ma1sdVersion() {
String mxidsVersion() {
def versionPattern = Pattern.compile("v(\\d+\\.)?(\\d+\\.)?(\\d+)(-.*)?")
String version = System.getenv('MA1SD_BUILD_VERSION')
String version = System.getenv('MXIDS_BUILD_VERSION')
if (version == null || version.size() == 0) {
version = gitVersion()
}
@@ -73,109 +73,114 @@ String gitVersion() {
buildscript {
repositories {
jcenter()
gradlePluginPortal()
mavenCentral()
google()
}
dependencies {
classpath 'com.github.jengelman.gradle.plugins:shadow:5.1.0'
classpath 'com.github.ben-manes:gradle-versions-plugin:0.27.0'
classpath 'com.github.johnrengelman:shadow:8.1.1'
classpath 'com.github.ben-manes:gradle-versions-plugin:0.52.0'
}
}
repositories {
jcenter()
mavenCentral()
}
dependencies {
// Logging
compile 'org.slf4j:slf4j-simple:1.7.25'
api 'org.slf4j:slf4j-simple:2.0.13'
// Easy file management
compile 'commons-io:commons-io:2.6'
api 'commons-io:commons-io:2.20.0'
// Config management
compile 'org.yaml:snakeyaml:1.25'
api 'org.yaml:snakeyaml:1.33'
// Dependencies from old Matrix-java-sdk
compile 'org.apache.commons:commons-lang3:3.9'
compile 'com.squareup.okhttp3:okhttp:4.2.2'
compile 'commons-codec:commons-codec:1.13'
api 'org.apache.commons:commons-lang3:3.18.0'
api 'com.squareup.okhttp3:okhttp:4.12.0'
api 'commons-codec:commons-codec:1.19.0'
// ORMLite
compile 'com.j256.ormlite:ormlite-jdbc:5.1'
api 'com.j256.ormlite:ormlite-jdbc:6.1'
// ed25519 handling
compile 'net.i2p.crypto:eddsa:0.3.0'
api 'net.i2p.crypto:eddsa:0.3.0'
// LDAP connector
compile 'org.apache.directory.api:api-all:1.0.0'
api 'org.apache.directory.api:api-all:2.1.7'
// DNS lookups
compile 'dnsjava:dnsjava:2.1.9'
api 'dnsjava:dnsjava:3.6.3'
// HTTP connections
compile 'org.apache.httpcomponents:httpclient:4.5.10'
api 'org.apache.httpcomponents:httpclient:4.5.14'
// Phone numbers validation
compile 'com.googlecode.libphonenumber:libphonenumber:8.10.22'
api 'com.googlecode.libphonenumber:libphonenumber:8.13.36'
// E-mail sending
compile 'javax.mail:javax.mail-api:1.6.2'
compile 'com.sun.mail:javax.mail:1.6.2'
api 'javax.mail:javax.mail-api:1.6.2'
api 'com.sun.mail:javax.mail:1.6.2'
// Google Firebase Authentication backend
compile 'com.google.firebase:firebase-admin:5.3.0'
api 'com.google.firebase:firebase-admin:9.2.0'
// Connection Pool
compile 'com.mchange:c3p0:0.9.5.4'
api 'com.mchange:c3p0:0.11.2'
// SQLite
compile 'org.xerial:sqlite-jdbc:3.28.0'
api 'org.xerial:sqlite-jdbc:3.50.3.0'
// PostgreSQL
compile 'org.postgresql:postgresql:42.2.8'
api 'org.postgresql:postgresql:42.7.7'
// MariaDB/MySQL
compile 'org.mariadb.jdbc:mariadb-java-client:2.5.1'
api 'org.mariadb.jdbc:mariadb-java-client:3.5.6'
// UNIX sockets
api 'com.kohlschutter.junixsocket:junixsocket-core:2.9.1'
// Twilio SDK for SMS
compile 'com.twilio.sdk:twilio:7.45.0'
api 'com.twilio.sdk:twilio:10.9.2'
// SendGrid SDK to send emails from GCE
compile 'com.sendgrid:sendgrid-java:2.2.2'
api 'com.sendgrid:sendgrid-java:4.10.2'
// ZT-Exec for exec identity store
compile 'org.zeroturnaround:zt-exec:1.11'
api 'org.zeroturnaround:zt-exec:1.12'
// HTTP server
compile 'io.undertow:undertow-core:2.0.27.Final'
api 'io.undertow:undertow-core:2.3.13.Final'
// Command parser for AS interface
implementation 'commons-cli:commons-cli:1.4'
api 'commons-cli:commons-cli:1.7.0'
testCompile 'junit:junit:4.13-rc-1'
testCompile 'com.github.tomakehurst:wiremock:2.25.1'
testCompile 'com.unboundid:unboundid-ldapsdk:4.0.12'
testCompile 'com.icegreen:greenmail:1.5.11'
testImplementation 'junit:junit:4.13.2'
testImplementation 'com.github.tomakehurst:wiremock:3.0.1'
testImplementation 'com.unboundid:unboundid-ldapsdk:7.0.0'
testImplementation 'com.icegreen:greenmail:1.6.15'
}
jar {
manifest {
attributes(
'Implementation-Version': ma1sdVersion()
'Implementation-Version': mxidsVersion()
)
}
}
shadowJar {
baseName = project.name
classifier = null
version = null
archiveBaseName.set(project.name)
archiveClassifier.set('') // Set to an empty string if you don't need a classifier.
archiveVersion.set('') // Set to an empty string if you don't want the version in the jar name.
}
task debBuild(dependsOn: shadowJar) {
doLast {
String debVersion = ma1sdVersion()
String debVersion = mxidsVersion()
println "Version for package: ${debVersion}"
mkdir distDir
mkdir debBuildBasePath
@@ -186,7 +191,7 @@ task debBuild(dependsOn: shadowJar) {
mkdir debBuildSystemdPath
copy {
from "${project.buildDir}/libs/ma1sd.jar"
from "${project.buildDir}/libs/mxids.jar"
into debBuildBinPath
}
@@ -234,12 +239,12 @@ task debBuild(dependsOn: shadowJar) {
ant.replace(
file: "${debBuildDebianPath}/postinst",
token: '%DEB_CONF_FILE%',
value: "${debConfPath}/ma1sd.yaml"
value: "${debConfPath}/mxids.yaml"
)
ant.chmod(
file: "${debBuildDebianPath}/postinst",
perm: 'a+x'
perm: '0755'
)
ant.chmod(
@@ -248,7 +253,7 @@ task debBuild(dependsOn: shadowJar) {
)
copy {
from "${project.file('src/systemd/ma1sd.service')}"
from "${project.file('src/systemd/mxids.service')}"
into debBuildSystemdPath
}
@@ -264,7 +269,7 @@ task debBuild(dependsOn: shadowJar) {
}
}
task dockerBuild(type: Exec, dependsOn: shadowJar) {
task dockerBuild(type: Exec) {
commandLine 'docker', 'build', '-t', dockerImageTag, project.rootDir
doLast {
@@ -274,6 +279,15 @@ task dockerBuild(type: Exec, dependsOn: shadowJar) {
}
}
task dockerBuildX(type: Exec, dependsOn: shadowJar) {
commandLine 'docker', 'buildx', 'build', '--push', '--platform', 'linux/arm64,linux/amd64,linux/arm/v7', '-t', dockerImageTag , project.rootDir
doLast {
exec {
commandLine 'docker', 'buildx', 'build', '--push', '--platform', 'linux/arm64,linux/amd64,linux/arm/v7', '-t', "${dockerImageName}:latest-dev", project.rootDir
}
}
}
task dockerPush(type: Exec) {
commandLine 'docker', 'push', dockerImageTag
@@ -283,3 +297,33 @@ task dockerPush(type: Exec) {
}
}
}
task dockerPushX(type: Exec) {
commandLine 'docker', 'push', dockerImageTag
doLast {
exec {
commandLine 'docker', 'push', "${dockerImageName}:latest-dev"
commandLine 'docker', 'push', "${dockerImageName}:latest-amd64-dev"
commandLine 'docker', 'push', "${dockerImageName}:latest-arm64-dev"
}
}
}
tasks.named('assemble').configure {
dependsOn shadowJar
}
tasks.withType(AbstractArchiveTask) {
if (it.name == 'distZip' || it.name == 'distTar') {
dependsOn shadowJar
}
}
tasks.named('startScripts').configure {
mustRunAfter shadowJar
}
tasks.named('startShadowScripts').configure {
dependsOn jar
}

View File

@@ -7,7 +7,7 @@ Default values:
```.yaml
matrix:
v1: true # deprecated
v2: true
v2: false
```
To disable change value to `false`.
@@ -19,8 +19,14 @@ matrix:
```
NOTE: Riot Web version 1.5.5 and below checks the v1 for backward compatibility.
NOTE: v2 disabled by default in order to preserve backward compatibility.
## Terms
###### Requires: No.
Administrator can omit terms configuration. In this case the terms checking will be disabled.
Example:
```.yaml
policy:
@@ -45,7 +51,7 @@ Where:
- `version` -- the terms version.
- `lang` -- the term language.
- `name` -- the name of the term.
- `url` -- the url of the term.
- `url` -- the url of the term. Might be any url (i.e. from another host) for a html page.
- `regexp` -- regexp patterns for API which should be available only after accepting the terms.
API will be checks for accepted terms only with authorization.
@@ -72,6 +78,10 @@ There is only one exception: [`POST /_matrix/identity/v2/terms`](https://matrix.
Hashes and the pepper updates together according to the `rotationPolicy`.
###### Requires: No.
In case the `none` algorithms ma1sd will be lookup using the v1 bulk API.
```.yaml
hashing:
enabled: true # enable or disable the hash lookup MSC2140 (default is false)
@@ -100,7 +110,7 @@ For sql provider (i.e. for the `synapseSql`):
```.yaml
synapseSql:
lookup:
query: 'select user_id as mxid, medium, address from user_threepids' # query for retrive 3PIDs for hashes.
query: 'select user_id as mxid, medium, address from user_threepid_id_server' # query for retrive 3PIDs for hashes.
```
For general sql provider:
@@ -126,5 +136,11 @@ exec:
hashEnabled: true # enable the hash lookup (defaults is false)
```
For ldap providers:
```.yaml
ldap:
lookup: true
```
NOTE: Federation requests work only with `none` algorithms.

View File

@@ -16,7 +16,7 @@ TCP 443
+<---------------------------------<+
|
| +-------------------+
TCP 8090 +-> | ma1sd |
TCP 8090 +-> | mxids |
| |
| - Profile's 3PIDs |
| - 3PID Invites |

View File

@@ -1,14 +1,18 @@
# From source
- [Binaries](#binaries)
- [Requirements](#requirements)
- [Build](#build)
- [Debian package](#debian-package)
- [Docker image](#docker-image)
- [Next steps](#next-steps)
- [From source](#from-source)
- [Binaries](#binaries)
- [Requirements](#requirements)
- [Build](#build)
- [Debian package](#debian-package)
- [Docker image](#docker-image)
- [Multi-platform builds](#multi-platform-builds)
- [Next steps](#next-steps)
## Binaries
### Requirements
- JDK 1.8
- OpenJDK 11
- OpenJDK 14
### Build
```bash
@@ -17,7 +21,7 @@ cd ma1sd
./gradlew build
```
Create a new configuration file by coping `ma1sd.example.yaml` to `ma1sd.yaml` and edit to your needs.
Create a new configuration file by coping `mxids.example.yaml` to `mxids.yaml` and edit to your needs.
For advanced configuration, see the [Configure section](configure.md).
Start the server in foreground to validate the build and configuration:
@@ -70,5 +74,13 @@ Then follow the instruction in the [Debian package](install/debian.md) document.
```
Then follow the instructions in the [Docker install](install/docker.md#configure) document.
### Multi-platform builds
Provided with experimental docker feature [buildx](https://docs.docker.com/buildx/working-with-buildx/)
To build the arm64 and amd64 images run:
```bash
./gradlew dockerBuildX
```
## Next steps
- [Integrate with your infrastructure](getting-started.md#integrate)

View File

@@ -58,7 +58,53 @@ Commonly the `server.publicUrl` should be the same value as the `trusted_third_p
## Storage
### SQLite
`storage.provider.sqlite.database`: Absolute location of the SQLite database
```yaml
storage:
backend: sqlite # default
provider:
sqlite:
database: /var/lib/ma1sd/store.db # Absolute location of the SQLite database
```
### Postgresql
```yaml
storage:
backend: postgresql
provider:
postgresql:
database: //localhost:5432/ma1sd
username: ma1sd
password: secret_password
```
See [the migration instruction](migration-to-postgresql.md) from sqlite to postgresql
## Logging
```yaml
logging:
root: error # default level for all loggers (apps and thirdparty libraries)
app: info # log level only for the ma1sd
requests: false # log request and response
```
Possible value: `trace`, `debug`, `info`, `warn`, `error`, `off`.
Default value for root level: `info`.
Value for app level can be specified via `MA1SD_LOG_LEVEL` environment variable, configuration or start options.
Default value for app level: `info`.
| start option | equivalent configuration |
| --- | --- |
| | app: info |
| -v | app: debug |
| -vv | app: trace |
#### WARNING
The setting `logging.requests` *MUST NOT* be used in production due it prints full unmasked request and response into the log and can be cause of the data leak.
This setting can be used only to testing and debugging errors.
## Identity stores
See the [Identity stores](stores/README.md) for specific configuration

View File

@@ -56,8 +56,7 @@ Accounts cannot currently migrate/move from one server to another.
See a [brief explanation document](concepts.md) about Matrix and ma1sd concepts and vocabulary.
### I already use the synapse LDAP3 auth provider. Why should I care about ma1sd?
The [synapse LDAP3 auth provider](https://github.com/matrix-org/matrix-synapse-ldap3) is not longer maintained despite
saying so and only handles on specific flow: validate credentials at login.
The [synapse LDAP3 auth provider](https://github.com/matrix-org/matrix-synapse-ldap3) only handles one specific flow: validate credentials at login.
It does not:
- Auto-provision user profiles

View File

@@ -1,5 +1,5 @@
# Identity
Implementation of the [Identity Service API r0.2.0](https://matrix.org/docs/spec/identity_service/r0.2.0.html).
Implementation of the [Identity Service API r0.3.0](https://matrix.org/docs/spec/identity_service/r0.3.0.html).
- [Lookups](#lookups)
- [Invitations](#invitations)

View File

@@ -121,15 +121,13 @@ server {
}
```
### Synapse
### Synapse (Deprecated with synapse v1.4.0)
Add your ma1sd domain into the `homeserver.yaml` at `trusted_third_party_id_servers` and restart synapse.
In a typical configuration, you would end up with something similar to:
```yaml
trusted_third_party_id_servers:
- matrix.example.org
```
It is **highly recommended** to remove `matrix.org` and `vector.im` (or any other default entry) from your configuration
so only your own Identity server is authoritative for your HS.
## Validate (Under reconstruction)
**NOTE:** In case your homeserver has no working federation, step 5 will not happen. If step 4 took place, consider

View File

@@ -0,0 +1,41 @@
# Migration from sqlite to postgresql
Starting from the version 2.3.0 ma1sd support postgresql for internal storage in addition to sqlite (parameters `storage.backend`).
#### Migration steps
1. create the postgresql database and user for ma1sd storage
2. create a backup for sqlite storage (default location: /var/lib/ma1sd/store.db)
3. migrate data from sqlite to postgresql
4. change ma1sd configuration to use the postgresql
For data migration is it possible to use https://pgloader.io tool.
Example of the migration command:
```shell script
pgloader --with "quote identifiers" /path/to/store.db pgsql://ma1sd_user:ma1sd_password@host:port/database
```
or (short version for database on localhost)
```shell script
pgloader --with "quote identifiers" /path/to/store.db pgsql://ma1sd_user:ma1sd_password@localhost/ma1sd
```
An option `--with "quote identifies"` used to create case sensitive tables.
ma1sd_user - postgresql user for ma1sd.
ma1sd_password - password of the postgresql user.
host - postgresql host
post - database port (default 5432)
database - database name.
Configuration example for postgresql storage:
```yaml
storage:
backend: postgresql
provider:
postgresql:
database: '//localhost/ma1sd' # or full variant //192.168.1.100:5432/ma1sd_database
username: 'ma1sd_user'
password: 'ma1sd_password'
```

View File

@@ -1,7 +1,10 @@
# Operations Guide
- [Overview](#overview)
- [Maintenance](#maintenance)
- [Backuo](#backup)
- [Operations Guide](#operations-guide)
- [Overview](#overview)
- [Maintenance](#maintenance)
- [Backup](#backup)
- [Run](#run)
- [Restore](#restore)
## Overview
This document gives various information for the day-to-day management and operations of ma1sd.

View File

@@ -89,7 +89,7 @@ ldap:
#### 3PIDs
You can also change the attribute lists for 3PID, like email or phone numbers.
The following example would overwrite the [default list of attributes](../../src/main/java/io/kamax/ma1sd/config/ldap/LdapConfig.java#L64)
The following example would overwrite the [default list of attributes](../../src/main/java/io/kamax/mxisd/config/ldap/LdapConfig.java#L64)
for emails and phone number:
```yaml
ldap:

View File

@@ -136,7 +136,7 @@ sql:
```
For the `role` query, `type` can be used to tell ma1sd how to inject the User ID in the query:
- `localpart` will extract and set only the localpart.
- `uid` will extract and set only the localpart.
- `mxid` will use the ID as-is.
On each query, the first parameter `?` is set as a string with the corresponding ID format.

Binary file not shown.

View File

@@ -1,6 +1,7 @@
#Thu Dec 05 22:39:36 MSK 2019
distributionUrl=https\://services.gradle.org/distributions/gradle-6.0-all.zip
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-all.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

299
gradlew vendored
View File

@@ -1,7 +1,7 @@
#!/usr/bin/env sh
#!/bin/sh
#
# Copyright 2015 the original author or authors.
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -15,80 +15,115 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
#
##############################################################################
##
## Gradle start up script for UN*X
##
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
MAX_FD=maximum
warn () {
echo "$*"
}
} >&2
die () {
echo
echo "$*"
echo
exit 1
}
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
CLASSPATH="\\\"\\\""
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD="$JAVA_HOME/bin/java"
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@@ -97,92 +132,120 @@ Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
JAVACMD=java
if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"

194
gradlew.bat vendored
View File

@@ -1,100 +1,94 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:execute
@rem Setup the command line
set CLASSPATH=
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

@@ -1,150 +0,0 @@
# Sample configuration file explaining the minimum required keys to be set to run ma1sd
#
# For a complete list of options, see https://github.com/ma1uta/ma1sd/docs/README.md
#
# Please follow the Getting Started guide if this is your first time using/configuring ma1sd
#
# -- https://github.com/ma1uta/ma1sd/blob/master/docs/getting-started.md#getting-started
#
#######################
# Matrix config items #
#######################
# Matrix domain, same as the domain configure in your Homeserver configuration.
# NOTE: in Synapse Homeserver, the Matrix domain is defined as 'server_name' in configuration file.
#
# This is used to build the various identifiers in all the features.
#
# If the hostname of the public URL used to reach your Matrix services is different from your Matrix domain,
# per example matrix.domain.tld vs domain.tld, then use the server.name configuration option.
# See the "Configure" section of the Getting Started guide for more info.
#
matrix:
domain: ''
v1: true # deprecated
v2: true # MSC2140 API v2
################
# Signing keys #
################
# Absolute path for the Identity Server signing keys database.
# /!\ THIS MUST **NOT** BE YOUR HOMESERVER KEYS FILE /!\
# If this path does not exist, it will be auto-generated.
#
# During testing, /var/tmp/ma1sd/keys is a possible value
# For production, recommended location shall be one of the following:
# - /var/lib/ma1sd/keys
# - /var/opt/ma1sd/keys
# - /var/local/ma1sd/keys
#
key:
path: ''
# Path to the SQLite DB file for ma1sd internal storage
# /!\ THIS MUST **NOT** BE YOUR HOMESERVER DATABASE /!\
#
# Examples:
# - /var/opt/ma1sd/store.db
# - /var/local/ma1sd/store.db
# - /var/lib/ma1sd/store.db
#
storage:
provider:
sqlite:
database: '/path/to/ma1sd.db'
###################
# Identity Stores #
###################
# If you are using synapse standalone and do not have an Identity store,
# see https://github.com/ma1uta/ma1sd/blob/master/docs/stores/synapse.md#synapse-identity-store
#
# If you would like to integrate with your AD/Samba/LDAP server,
# see https://github.com/ma1uta/ma1sd/blob/master/docs/stores/ldap.md
#
# For any other Identity store, or to simply discover them,
# see https://github.com/ma1uta/ma1sd/blob/master/docs/stores/README.md
#################################################
# Notifications for invites/addition to profile #
#################################################
# This is mandatory to deal with anything e-mail related.
#
# For an introduction to sessions, invites and 3PIDs in general,
# see https://github.com/ma1uta/ma1sd/blob/master/docs/threepids/session/session.md#3pid-sessions
#
# If you would like to change the content of the notifications,
# see https://github.com/ma1uta/ma1sd/blob/master/docs/threepids/notification/template-generator.md
#
#### E-mail connector
threepid:
medium:
email:
identity:
# The e-mail to send as.
from: "matrix-identity@example.org"
connectors:
smtp:
# SMTP host
host: "smtp.example.org"
# TLS mode for the connection
# Possible values:
# 0 Disable any kind of TLS entirely
# 1 Enable STARTLS if supported by server (default)
# 2 Force STARTLS and fail if not available
# 3 Use full TLS/SSL instead of STARTLS
#
tls: 1
# SMTP port
# Be sure to adapt depending on your TLS choice, if changed from default
port: 587
# Login for SMTP
login: "matrix-identity@example.org"
# Password for the account
password: "ThePassword"
#### MSC2134 (hash lookup)
hashing:
enabled: false # enable or disable the hash lookup MSC2140 (default is false)
pepperLength: 20 # length of the pepper value (default is 20)
rotationPolicy: per_requests # or `per_seconds` how often the hashes will be updating
hashStorageType: sql # or `in_memory` where the hashes will be stored
algorithms:
- none # the same as v1 bulk lookup
- sha256 # hash the 3PID and pepper.
delay: 2m # how often hashes will be updated if rotation policy = per_seconds (default is 10s)
requests: 10 # how many lookup requests will be performed before updating hashes if rotation policy = per_requests (default is 10)
### hash lookup for synapseSql provider.
# synapseSql:
# lookup:
# query: 'select user_id as mxid, medium, address from user_threepids' # query for retrive 3PIDs for hashes.
#### MSC2140 (Terms)
policy:
policies:
term_name: # term name
version: 1.0 # version
terms:
en: # lang
name: term name en # localized name
url: https://ma1sd.host.tld/term_en.html # localized url
fe: # lang
name: term name fr # localized name
url: https://ma1sd.host.tld/term_fr.html # localized url
regexp:
- '/_matrix/identity/v2/account.*'
- '/_matrix/identity/v2/hash_details'
- '/_matrix/identity/v2/lookup'

219
mxids.example.yaml Normal file
View File

@@ -0,0 +1,219 @@
# Sample configuration file explaining the minimum required keys to be set to run ma1sd
#
# For a complete list of options, see https://github.com/ma1uta/ma1sd/docs/README.md
#
# Please follow the Getting Started guide if this is your first time using/configuring ma1sd
#
# -- https://github.com/ma1uta/ma1sd/blob/master/docs/getting-started.md#getting-started
#
#######################
# Matrix config items #
#######################
# Matrix domain, same as the domain configure in your Homeserver configuration.
# NOTE: in Synapse Homeserver, the Matrix domain is defined as 'server_name' in configuration file.
#
# This is used to build the various identifiers in all the features.
#
# If the hostname of the public URL used to reach your Matrix services is different from your Matrix domain,
# per example matrix.domain.tld vs domain.tld, then use the server.name configuration option.
# See the "Configure" section of the Getting Started guide for more info.
#
matrix:
domain: ''
v1: false # deprecated
v2: true # MSC2140 API v2. Riot require enabled V2 API.
################
# Signing keys #
################
# Absolute path for the Identity Server signing keys database.
# /!\ THIS MUST **NOT** BE YOUR HOMESERVER KEYS FILE /!\
# If this path does not exist, it will be auto-generated.
#
# During testing, /var/tmp/mxids/keys is a possible value
# For production, recommended location shall be one of the following:
# - /var/lib/mxids/keys
# - /var/opt/mxids/keys
# - /var/local/mxids/keys
#
key:
path: ''
# Path to the SQLite DB file for ma1sd internal storage
# /!\ THIS MUST **NOT** BE YOUR HOMESERVER DATABASE /!\
#
# Examples:
# - /var/opt/mxids/store.db
# - /var/local/mxids/store.db
# - /var/lib/mxids/store.db
#
storage:
# backend: sqlite # or postgresql
provider:
sqlite:
database: '/path/to/mxids.db'
# postgresql:
# # Wrap all string values with quotes to avoid yaml parsing mistakes
# database: '//localhost/mxids' # or full variant //192.168.1.100:5432/mxids_database
# username: 'mxids_user'
# password: 'mxids_password'
#
# # Pool configuration for postgresql backend.
# #######
# # Enable or disable pooling
# pool: false
#
# #######
# # Check database connection before get from pool
# testBeforeGetFromPool: false # or true
#
# #######
# # There is an internal thread which checks each of the database connections as a keep-alive mechanism. This set the
# # number of milliseconds it sleeps between checks -- default is 30000. To disable the checking thread, set this to
# # 0 before you start using the connection source.
# checkConnectionsEveryMillis: 30000
#
# #######
# # Set the number of connections that can be unused in the available list.
# maxConnectionsFree: 5
#
# #######
# # Set the number of milliseconds that a connection can stay open before being closed. Set to 9223372036854775807 to have
# # the connections never expire.
# maxConnectionAgeMillis: 3600000
###################
# Identity Stores #
###################
# If you are using synapse standalone and do not have an Identity store,
# see https://github.com/ma1uta/ma1sd/blob/master/docs/stores/synapse.md#synapse-identity-store
#
# If you would like to integrate with your AD/Samba/LDAP server,
# see https://github.com/ma1uta/ma1sd/blob/master/docs/stores/ldap.md
#
# For any other Identity store, or to simply discover them,
# see https://github.com/ma1uta/ma1sd/blob/master/docs/stores/README.md
#################################################
# Notifications for invites/addition to profile #
#################################################
# This is mandatory to deal with anything e-mail related.
#
# For an introduction to sessions, invites and 3PIDs in general,
# see https://github.com/ma1uta/ma1sd/blob/master/docs/threepids/session/session.md#3pid-sessions
#
# If you would like to change the content of the notifications,
# see https://github.com/ma1uta/ma1sd/blob/master/docs/threepids/notification/template-generator.md
#
#### E-mail connector
threepid:
medium:
email:
identity:
# The e-mail to send as.
from: "matrix-identity@example.org"
connectors:
smtp:
# SMTP host
host: "smtp.example.org"
# TLS mode for the connection
# Possible values:
# 0 Disable any kind of TLS entirely
# 1 Enable STARTLS if supported by server (default)
# 2 Force STARTLS and fail if not available
# 3 Use full TLS/SSL instead of STARTLS
#
tls: 1
# SMTP port
# Be sure to adapt depending on your TLS choice, if changed from default
port: 587
# Login for SMTP
login: "matrix-identity@example.org"
# Password for the account
password: "ThePassword"
#### MSC2134 (hash lookup)
#hashing:
# enabled: false # enable or disable the hash lookup MSC2140 (default is false)
# pepperLength: 20 # length of the pepper value (default is 20)
# rotationPolicy: per_requests # or `per_seconds` how often the hashes will be updating
# hashStorageType: sql # or `in_memory` where the hashes will be stored
# algorithms:
# - none # the same as v1 bulk lookup
# - sha256 # hash the 3PID and pepper.
# delay: 2m # how often hashes will be updated if rotation policy = per_seconds (default is 10s)
# requests: 10 # how many lookup requests will be performed before updating hashes if rotation policy = per_requests (default is 10)
### hash lookup for synapseSql provider.
# synapseSql:
# lookup:
# query: 'select user_id as mxid, medium, address from user_threepid_id_server' # query for retrive 3PIDs for hashes.
# legacyRoomNames: false # use the old query to get room names.
### hash lookup for ldap provider (with example of the ldap configuration)
# ldap:
# enabled: true
# lookup: true # hash lookup
# activeDirectory: false
# defaultDomain: ''
# connection:
# host: 'ldap.domain.tld'
# port: 389
# bindDn: 'cn=admin,dc=domain,dc=tld'
# bindPassword: 'Secret'
# baseDNs:
# - 'dc=domain,dc=tld'
# attribute:
# uid:
# type: 'uid' # or mxid
# value: 'cn'
# name: 'displayName'
# identity:
# filter: '(objectClass=inetOrgPerson)'
#### MSC2140 (Terms)
#policy:
# policies:
# term_name: # term name
# version: 1.0 # version
# terms:
# en: # lang
# name: term name en # localized name
# url: https://mxids.host.tld/term_en.html # localized url
# fe: # lang
# name: term name fr # localized name
# url: https://mxids.host.tld/term_fr.html # localized url
# regexp:
# - '/_matrix/identity/v2/account.*'
# - '/_matrix/identity/v2/hash_details'
# - '/_matrix/identity/v2/lookup'
#
# logging:
# root: error # default level for all loggers (apps and thirdparty libraries)
# app: info # log level only for the ma1sd
# requests: false # or true to dump full requests and responses
# Config invitation manager
#invite:
# fullDisplayName: true # print full name of the invited user (default false)
# resolution:
# timer: 10
# period: seconds # search invites every 10 seconds (by default 5 minutes)
# Internal API
#internal:
# enabled: true # default to false

11
renovate.json Normal file
View File

@@ -0,0 +1,11 @@
{
"extends": [
"config:base"
],
"packageRules": [
{
"updateTypes": ["minor", "patch", "pin", "digest"],
"automerge": true
}
]
}

View File

@@ -1,6 +1,6 @@
Package: ma1sd
Package: mxids
Maintainer: ma1uta <sablintolya@gmail.com>
Homepage: https://github.com/ma1uta/ma1sd
Homepage: https://git.cqre.net/cqrenet/mxids.git
Description: Federated Matrix Identity Server
Architecture: all
Section: net

View File

@@ -1,19 +1,19 @@
#!/bin/bash -e
# Add service account
useradd -r ma1sd || true
useradd -r mxids || true
# Set permissions for data directory
chown -R ma1sd:ma1sd %DEB_DATA_DIR%
chown -R mxids:mxids %DEB_DATA_DIR%
# Create symlink to ma1sd run script
ln -sfT /usr/lib/ma1sd/ma1sd /usr/bin/ma1sd
# Create symlink to mxids run script
ln -sfT /usr/lib/mxids/mxids /usr/bin/mxids
# Enable systemd service
systemctl enable ma1sd.service
systemctl enable mxids.service
# If we already have a config file setup, we attempt to run ma1sd automatically
# If we already have a config file setup, we attempt to run mxids automatically
# Specifically targeted at upgrades where the service needs to be restarted
if [ -f "%DEB_CONF_FILE%" ]; then
systemctl restart ma1sd.service
systemctl restart mxids.service
fi

View File

@@ -1,10 +1,10 @@
#!/bin/bash
# Stop running instance if needed
systemctl stop ma1sd.service
systemctl stop mxids.service
# Disable service if exists
systemctl disable ma1sd.service
systemctl disable mxids.service
# remove symlink
rm /usr/bin/ma1sd
rm /usr/bin/mxids

View File

@@ -27,8 +27,8 @@ if [[ -n "$CONF_FILE_PATH" ]] && [ ! -f "$CONF_FILE_PATH" ]; then
echo >> "$CONF_FILE_PATH"
fi
echo "Starting ma1sd..."
echo "Starting mxids..."
echo
fi
exec java -jar /app/ma1sd.jar -c /etc/ma1sd/ma1sd.yaml
exec java -jar /app/mxids.jar -c /etc/mxids/mxids.yaml

View File

@@ -53,7 +53,7 @@ public class MatrixPath {
}
public static MatrixPath clientR0() {
return client().add("r0");
return client().add("v3");
}
private StringBuilder path = new StringBuilder();

View File

@@ -27,7 +27,7 @@ public class ThreePid implements _ThreePid {
public ThreePid(String medium, String address) {
this.medium = medium;
this.address = address;
this.address = address.toLowerCase();
}
@Override

View File

@@ -375,13 +375,13 @@ public abstract class AMatrixHttpClient implements _MatrixClientRaw {
}
protected HttpUrl.Builder getClientPathBuilder(String... segments) {
String[] base = { "client", "r0" };
String[] base = { "client", "v3" };
segments = ArrayUtils.addAll(base, segments);
return getPathBuilder(segments);
}
protected HttpUrl.Builder getMediaPathBuilder(String... segments) {
String[] base = { "media", "r0" };
String[] base = { "media", "v3" };
segments = ArrayUtils.addAll(base, segments);
return getPathBuilder(segments);
}

View File

@@ -23,11 +23,13 @@ package io.kamax.mxisd;
import io.kamax.mxisd.config.MatrixConfig;
import io.kamax.mxisd.config.MxisdConfig;
import io.kamax.mxisd.config.PolicyConfig;
import io.kamax.mxisd.config.ServerConfig;
import io.kamax.mxisd.http.undertow.handler.ApiHandler;
import io.kamax.mxisd.http.undertow.handler.AuthorizationHandler;
import io.kamax.mxisd.http.undertow.handler.CheckTermsHandler;
import io.kamax.mxisd.http.undertow.handler.InternalInfoHandler;
import io.kamax.mxisd.http.undertow.handler.OptionsHandler;
import io.kamax.mxisd.http.undertow.handler.RequestDumpingHandler;
import io.kamax.mxisd.http.undertow.handler.SaneHandler;
import io.kamax.mxisd.http.undertow.handler.as.v1.AsNotFoundHandler;
import io.kamax.mxisd.http.undertow.handler.as.v1.AsTransactionHandler;
@@ -56,6 +58,7 @@ import io.kamax.mxisd.http.undertow.handler.identity.v1.BulkLookupHandler;
import io.kamax.mxisd.http.undertow.handler.identity.v1.SingleLookupHandler;
import io.kamax.mxisd.http.undertow.handler.identity.v2.HashDetailsHandler;
import io.kamax.mxisd.http.undertow.handler.identity.v2.HashLookupHandler;
import io.kamax.mxisd.http.undertow.handler.internal.InternalInviteManagerHandler;
import io.kamax.mxisd.http.undertow.handler.invite.v1.RoomInviteHandler;
import io.kamax.mxisd.http.undertow.handler.profile.v1.InternalProfileHandler;
import io.kamax.mxisd.http.undertow.handler.profile.v1.ProfileHandler;
@@ -99,35 +102,35 @@ public class HttpMxisd {
public void start() {
m.start();
HttpHandler asUserHandler = SaneHandler.around(new AsUserHandler(m.getAs()));
HttpHandler asTxnHandler = SaneHandler.around(new AsTransactionHandler(m.getAs()));
HttpHandler asNotFoundHandler = SaneHandler.around(new AsNotFoundHandler(m.getAs()));
HttpHandler asUserHandler = sane(new AsUserHandler(m.getAs()));
HttpHandler asTxnHandler = sane(new AsTransactionHandler(m.getAs()));
HttpHandler asNotFoundHandler = sane(new AsNotFoundHandler(m.getAs()));
final RoutingHandler handler = Handlers.routing()
.add("OPTIONS", "/**", SaneHandler.around(new OptionsHandler()))
.add("OPTIONS", "/**", sane(new OptionsHandler()))
// Status endpoints
.get(StatusHandler.Path, SaneHandler.around(new StatusHandler()))
.get(VersionHandler.Path, SaneHandler.around(new VersionHandler()))
.get(StatusHandler.Path, sane(new StatusHandler()))
.get(VersionHandler.Path, sane(new VersionHandler()))
// Authentication endpoints
.get(LoginHandler.Path, SaneHandler.around(new LoginGetHandler(m.getAuth(), m.getHttpClient())))
.post(LoginHandler.Path, SaneHandler.around(new LoginPostHandler(m.getAuth())))
.post(RestAuthHandler.Path, SaneHandler.around(new RestAuthHandler(m.getAuth())))
.get(LoginHandler.Path, sane(new LoginGetHandler(m.getAuth(), m.getHttpClient())))
.post(LoginHandler.Path, sane(new LoginPostHandler(m.getAuth())))
.post(RestAuthHandler.Path, sane(new RestAuthHandler(m.getAuth())))
// Directory endpoints
.post(UserDirectorySearchHandler.Path, SaneHandler.around(new UserDirectorySearchHandler(m.getDirectory())))
.post(UserDirectorySearchHandler.Path, sane(new UserDirectorySearchHandler(m.getDirectory())))
// Profile endpoints
.get(ProfileHandler.Path, SaneHandler.around(new ProfileHandler(m.getProfile())))
.get(InternalProfileHandler.Path, SaneHandler.around(new InternalProfileHandler(m.getProfile())))
.get(ProfileHandler.Path, sane(new ProfileHandler(m.getProfile())))
.get(InternalProfileHandler.Path, sane(new InternalProfileHandler(m.getProfile())))
// Registration endpoints
.post(Register3pidRequestTokenHandler.Path,
SaneHandler.around(new Register3pidRequestTokenHandler(m.getReg(), m.getClientDns(), m.getHttpClient())))
sane(new Register3pidRequestTokenHandler(m.getReg(), m.getClientDns(), m.getHttpClient())))
// Invite endpoints
.post(RoomInviteHandler.Path, SaneHandler.around(new RoomInviteHandler(m.getHttpClient(), m.getClientDns(), m.getInvite())))
.post(RoomInviteHandler.Path, sane(new RoomInviteHandler(m.getHttpClient(), m.getClientDns(), m.getInvite())))
// Application Service endpoints
.get(AsUserHandler.Path, asUserHandler)
@@ -139,13 +142,19 @@ public class HttpMxisd {
.put("/transactions/{" + AsTransactionHandler.ID + "}", asTxnHandler) // Legacy endpoint
// Banned endpoints
.get(InternalInfoHandler.Path, SaneHandler.around(new InternalInfoHandler()));
.get(InternalInfoHandler.Path, sane(new InternalInfoHandler()));
keyEndpoints(handler);
identityEndpoints(handler);
termsEndpoints(handler);
hashEndpoints(handler);
accountEndpoints(handler);
httpSrv = Undertow.builder().addHttpListener(m.getConfig().getServer().getPort(), "0.0.0.0").setHandler(handler).build();
if (m.getConfig().getInternal().isEnabled()) {
handler.get(InternalInviteManagerHandler.PATH, new InternalInviteManagerHandler(m.getInvite()));
}
ServerConfig serverConfig = m.getConfig().getServer();
httpSrv = Undertow.builder().addHttpListener(serverConfig.getPort(), serverConfig.getHostname()).setHandler(handler).build();
httpSrv.start();
}
@@ -189,23 +198,32 @@ public class HttpMxisd {
}
private void accountEndpoints(RoutingHandler routingHandler) {
routingHandler.post(AccountRegisterHandler.Path, SaneHandler.around(new AccountRegisterHandler(m.getAccMgr())));
wrapWithTokenAndAuthorizationHandlers(routingHandler, Methods.GET, sane(new AccountGetUserInfoHandler(m.getAccMgr())),
AccountGetUserInfoHandler.Path, true);
wrapWithTokenAndAuthorizationHandlers(routingHandler, Methods.GET, sane(new AccountLogoutHandler(m.getAccMgr())),
AccountLogoutHandler.Path, true);
MatrixConfig matrixConfig = m.getConfig().getMatrix();
if (matrixConfig.isV2()) {
routingHandler.post(AccountRegisterHandler.Path, sane(new AccountRegisterHandler(m.getAccMgr())));
wrapWithTokenAndAuthorizationHandlers(routingHandler, Methods.GET, new AccountGetUserInfoHandler(m.getAccMgr()),
AccountGetUserInfoHandler.Path, true);
wrapWithTokenAndAuthorizationHandlers(routingHandler, Methods.GET, new AccountLogoutHandler(m.getAccMgr()),
AccountLogoutHandler.Path, true);
}
}
private void termsEndpoints(RoutingHandler routingHandler) {
routingHandler.get(GetTermsHandler.PATH, new GetTermsHandler(m.getConfig().getPolicy()));
routingHandler.post(AcceptTermsHandler.PATH, sane(new AcceptTermsHandler(m.getAccMgr())));
MatrixConfig matrixConfig = m.getConfig().getMatrix();
if (matrixConfig.isV2()) {
routingHandler.get(GetTermsHandler.PATH, sane(new GetTermsHandler(m.getConfig().getPolicy())));
routingHandler.post(AcceptTermsHandler.PATH, sane(new AcceptTermsHandler(m.getAccMgr())));
}
}
private void hashEndpoints(RoutingHandler routingHandler) {
wrapWithTokenAndAuthorizationHandlers(routingHandler, Methods.GET, sane(new HashDetailsHandler(m.getHashManager())),
HashDetailsHandler.PATH, true);
wrapWithTokenAndAuthorizationHandlers(routingHandler, Methods.POST,
sane(new HashLookupHandler(m.getIdentity(), m.getHashManager())), HashLookupHandler.Path, true);
MatrixConfig matrixConfig = m.getConfig().getMatrix();
if (matrixConfig.isV2()) {
wrapWithTokenAndAuthorizationHandlers(routingHandler, Methods.GET, new HashDetailsHandler(m.getHashManager()),
HashDetailsHandler.PATH, true);
wrapWithTokenAndAuthorizationHandlers(routingHandler, Methods.POST,
new HashLookupHandler(m.getIdentity(), m.getHashManager()), HashLookupHandler.Path, true);
}
}
private void addEndpoints(RoutingHandler routingHandler, HttpString method, boolean useAuthorization, ApiHandler... handlers) {
@@ -218,11 +236,11 @@ public class HttpMxisd {
HttpHandler httpHandler) {
MatrixConfig matrixConfig = m.getConfig().getMatrix();
if (matrixConfig.isV1()) {
routingHandler.add(method, apiHandler.getPath(IdentityServiceAPI.V1), httpHandler);
routingHandler.add(method, apiHandler.getPath(IdentityServiceAPI.V1), sane(httpHandler));
}
if (matrixConfig.isV2()) {
String path = apiHandler.getPath(IdentityServiceAPI.V2);
wrapWithTokenAndAuthorizationHandlers(routingHandler, method, httpHandler, path, useAuthorization);
wrapWithTokenAndAuthorizationHandlers(routingHandler, method, httpHandler, apiHandler.getPath(IdentityServiceAPI.V2),
useAuthorization);
}
}
@@ -236,7 +254,7 @@ public class HttpMxisd {
} else {
wrappedHandler = httpHandler;
}
routingHandler.add(method, url, wrappedHandler);
routingHandler.add(method, url, sane(wrappedHandler));
}
@NotNull
@@ -256,6 +274,11 @@ public class HttpMxisd {
}
private HttpHandler sane(HttpHandler httpHandler) {
return SaneHandler.around(httpHandler);
SaneHandler handler = SaneHandler.around(httpHandler);
if (m.getConfig().getLogging().isRequests()) {
return new RequestDumpingHandler(handler);
} else {
return handler;
}
}
}

View File

@@ -27,6 +27,7 @@ import io.kamax.mxisd.auth.AuthProviders;
import io.kamax.mxisd.backend.IdentityStoreSupplier;
import io.kamax.mxisd.backend.sql.synapse.Synapse;
import io.kamax.mxisd.config.MxisdConfig;
import io.kamax.mxisd.config.StorageConfig;
import io.kamax.mxisd.crypto.CryptoFactory;
import io.kamax.mxisd.crypto.KeyManager;
import io.kamax.mxisd.crypto.SignatureManager;
@@ -54,7 +55,7 @@ import io.kamax.mxisd.registration.RegistrationManager;
import io.kamax.mxisd.session.SessionManager;
import io.kamax.mxisd.storage.IStorage;
import io.kamax.mxisd.storage.ormlite.OrmLiteSqlStorage;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
@@ -66,7 +67,7 @@ public class Mxisd {
public static final String Version = StringUtils.defaultIfBlank(Mxisd.class.getPackage().getImplementationVersion(), "UNKNOWN");
public static final String Agent = Name + "/" + Version;
private MxisdConfig cfg;
private final MxisdConfig cfg;
private CloseableHttpClient httpClient;
private IRemoteIdentityServerFetcher srvFetcher;
@@ -109,7 +110,10 @@ public class Mxisd {
IdentityServerUtils.setHttpClient(httpClient);
srvFetcher = new RemoteIdentityServerFetcher(httpClient);
store = new OrmLiteSqlStorage(cfg);
StorageConfig.BackendEnum storageBackend = cfg.getStorage().getBackend();
StorageConfig.Provider storageProvider = cfg.getStorage().getProvider();
store = new OrmLiteSqlStorage(storageBackend, storageProvider);
keyMgr = CryptoFactory.getKeyManager(cfg.getKey());
signMgr = CryptoFactory.getSignatureManager(cfg, keyMgr);
clientDns = new ClientDnsOverwrite(cfg.getDns().getOverwrite());

View File

@@ -36,7 +36,7 @@ public class MxisdStandaloneExec {
private static final Logger log = LoggerFactory.getLogger("App");
public static void main(String[] args) {
String logLevel = System.getenv("MA1SD_LOG_LEVEL");
String logLevel = System.getenv("MXIDS_LOG_LEVEL");
if (StringUtils.isNotBlank(logLevel)) {
System.setProperty("org.slf4j.simpleLogger.log.io.kamax.mxisd", logLevel);
}
@@ -44,49 +44,72 @@ public class MxisdStandaloneExec {
try {
MxisdConfig cfg = null;
Iterator<String> argsIt = Arrays.asList(args).iterator();
boolean dump = false;
boolean exit = false;
while (argsIt.hasNext()) {
String arg = argsIt.next();
if (StringUtils.equalsAny(arg, "-h", "--help", "-?", "--usage")) {
System.out.println("Available arguments:" + System.lineSeparator());
System.out.println(" -h, --help Show this help message");
System.out.println(" --version Print the version then exit");
System.out.println(" -c, --config Set the configuration file location");
System.out.println(" -v Increase log level (log more info)");
System.out.println(" -vv Further increase log level");
System.out.println(" ");
System.exit(0);
} else if (StringUtils.equals(arg, "-v")) {
System.setProperty("org.slf4j.simpleLogger.log.io.kamax.mxisd", "debug");
} else if (StringUtils.equals(arg, "-vv")) {
System.setProperty("org.slf4j.simpleLogger.log.io.kamax.mxisd", "trace");
} else if (StringUtils.equalsAny(arg, "-c", "--config")) {
String cfgFile = argsIt.next();
cfg = YamlConfigLoader.loadFromFile(cfgFile);
} else if (StringUtils.equals("--version", arg)) {
System.out.println(Mxisd.Version);
System.exit(0);
} else {
System.err.println("Invalid argument: " + arg);
System.err.println("Try '--help' for available arguments");
System.exit(1);
switch (arg) {
case "-h":
case "--help":
case "-?":
case "--usage":
System.out.println("Available arguments:" + System.lineSeparator());
System.out.println(" -h, --help Show this help message");
System.out.println(" --version Print the version then exit");
System.out.println(" -c, --config Set the configuration file location");
System.out.println(" -v Increase log level (log more info)");
System.out.println(" -vv Further increase log level");
System.out.println(" --dump Dump the full mxids configuration");
System.out.println(" --dump-and-exit Dump the full mxids configuration and exit");
System.out.println(" ");
System.exit(0);
return;
case "-v":
System.setProperty("org.slf4j.simpleLogger.log.io.kamax.mxisd", "debug");
break;
case "-vv":
System.setProperty("org.slf4j.simpleLogger.log.io.kamax.mxisd", "trace");
break;
case "-c":
case "--config":
String cfgFile = argsIt.next();
cfg = YamlConfigLoader.loadFromFile(cfgFile);
break;
case "--dump-and-exit":
exit = true;
case "--dump":
dump = true;
break;
default:
System.err.println("Invalid argument: " + arg);
System.err.println("Try '--help' for available arguments");
System.exit(1);
}
}
if (Objects.isNull(cfg)) {
cfg = YamlConfigLoader.tryLoadFromFile("ma1sd.yaml").orElseGet(MxisdConfig::new);
cfg = YamlConfigLoader.tryLoadFromFile("mxids.yaml").orElseGet(MxisdConfig::new);
}
log.info("ma1sd starting");
if (dump) {
String outputPath = "mxids.yaml";
YamlConfigLoader.dumpConfig(cfg, outputPath);
if (exit) {
System.exit(0);
}
}
log.info("mxids starting");
log.info("Version: {}", Mxisd.Version);
HttpMxisd mxisd = new HttpMxisd(cfg);
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
mxisd.stop();
log.info("ma1sd stopped");
log.info("mxids stopped");
}));
mxisd.start();
log.info("ma1sd started");
log.info("mxids started");
} catch (ConfigurationException e) {
log.error(e.getDetailedMessage());
log.error(e.getMessage());

View File

@@ -20,7 +20,7 @@
package io.kamax.mxisd;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.StringUtils;
// FIXME consider integrating in matrix-java-sdk?
public enum UserIdType {

View File

@@ -48,6 +48,7 @@ import org.slf4j.LoggerFactory;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.introspector.BeanAccess;
import org.yaml.snakeyaml.representer.Representer;
import org.yaml.snakeyaml.DumperOptions;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -142,18 +143,18 @@ public class AppSvcManager {
String synapseRegFile = cfg.getAppsvc().getRegistration().getSynapse().getFile();
if (StringUtils.isBlank(synapseRegFile)) {
log.info("No synapse registration file path given - skipping generation...");
return;
log.info("No synapse registration file path given - skipping generation...");
return;
}
SynapseRegistrationYaml syncCfg = SynapseRegistrationYaml.parse(cfg.getAppsvc(), cfg.getMatrix().getDomain());
Representer rep = new Representer();
DumperOptions options = new DumperOptions();
options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); // Set YAML flow style to block
Representer rep = new Representer(options);
rep.getPropertyUtils().setBeanAccess(BeanAccess.FIELD);
Yaml yaml = new Yaml(rep);
// SnakeYAML set the type of object on the first line, which can fail to be parsed on synapse
// We therefore need to split the resulting string, remove the first line, and then write it
List<String> lines = new ArrayList<>(Arrays.asList(yaml.dump(syncCfg).split("\\R+")));
if (StringUtils.equals(lines.get(0), "!!" + SynapseRegistrationYaml.class.getCanonicalName())) {
lines.remove(0);

View File

@@ -25,8 +25,8 @@ import io.kamax.matrix.hs._MatrixRoom;
import io.kamax.mxisd.Mxisd;
import io.kamax.mxisd.invitation.IThreePidInviteReply;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.text.StrBuilder;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.text.StrBuilder;
import java.util.List;

View File

@@ -25,7 +25,7 @@ import io.kamax.matrix.hs._MatrixRoom;
import io.kamax.mxisd.Mxisd;
import io.kamax.mxisd.lookup.SingleLookupReply;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.lang.text.StrBuilder;
import org.apache.commons.lang3.text.StrBuilder;
import org.apache.commons.lang3.StringUtils;
import java.util.Optional;

View File

@@ -34,7 +34,7 @@ import io.kamax.mxisd.invitation.IMatrixIdInvite;
import io.kamax.mxisd.invitation.MatrixIdInvite;
import io.kamax.mxisd.notification.NotificationManager;
import io.kamax.mxisd.profile.ProfileManager;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -144,7 +144,13 @@ public class MembershipEventProcessor implements EventTypeProcessor {
.collect(Collectors.toList());
log.info("Found {} email(s) in identity store for {}", tpids.size(), inviteeId);
for (_ThreePid tpid : tpids) {
log.info("Removing duplicates from identity store");
List<_ThreePid> uniqueTpids = tpids.stream()
.distinct()
.collect(Collectors.toList());
log.info("There are {} unique email(s) in identity store for {}", uniqueTpids.size(), inviteeId);
for (_ThreePid tpid : uniqueTpids) {
log.info("Found Email to notify about room invitation: {}", tpid.getAddress());
Map<String, String> properties = new HashMap<>();
profiler.getDisplayName(sender).ifPresent(name -> properties.put("sender_display_name", name));

View File

@@ -32,7 +32,7 @@ import io.kamax.mxisd.as.processor.command.InviteCommandProcessor;
import io.kamax.mxisd.as.processor.command.LookupCommandProcessor;
import io.kamax.mxisd.as.processor.command.PingCommandProcessor;
import org.apache.commons.cli.*;
import org.apache.commons.lang.text.StrBuilder;
import org.apache.commons.lang3.text.StrBuilder;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

View File

@@ -10,6 +10,7 @@ import io.kamax.mxisd.exception.BadRequestException;
import io.kamax.mxisd.exception.InvalidCredentialsException;
import io.kamax.mxisd.exception.NotFoundException;
import io.kamax.mxisd.matrix.HomeserverFederationResolver;
import io.kamax.mxisd.matrix.HomeserverVerifier;
import io.kamax.mxisd.storage.IStorage;
import io.kamax.mxisd.storage.ormlite.dao.AccountDao;
import org.apache.http.HttpStatus;
@@ -22,18 +23,10 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.security.cert.Certificate;
import java.security.cert.CertificateParsingException;
import java.security.cert.X509Certificate;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLSession;
public class AccountManager {
@@ -80,7 +73,7 @@ public class AccountManager {
homeserverURL + "/_matrix/federation/v1/openid/userinfo?access_token=" + openIdToken.getAccessToken());
String userId;
try (CloseableHttpClient httpClient = HttpClients.custom()
.setSSLHostnameVerifier(new MatrixHostnameVerifier(homeserverTarget.getDomain())).build()) {
.setSSLHostnameVerifier(new HomeserverVerifier(homeserverTarget.getDomain())).build()) {
try (CloseableHttpResponse response = httpClient.execute(getUserInfo)) {
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == HttpStatus.SC_OK) {
@@ -170,74 +163,4 @@ public class AccountManager {
public MatrixConfig getMatrixConfig() {
return matrixConfig;
}
public static class MatrixHostnameVerifier implements HostnameVerifier {
private static final String ALT_DNS_NAME_TYPE = "2";
private static final String ALT_IP_ADDRESS_TYPE = "7";
private final String matrixHostname;
public MatrixHostnameVerifier(String matrixHostname) {
this.matrixHostname = matrixHostname;
}
@Override
public boolean verify(String hostname, SSLSession session) {
try {
Certificate peerCertificate = session.getPeerCertificates()[0];
if (peerCertificate instanceof X509Certificate) {
X509Certificate x509Certificate = (X509Certificate) peerCertificate;
if (x509Certificate.getSubjectAlternativeNames() == null) {
return false;
}
for (String altSubjectName : getAltSubjectNames(x509Certificate)) {
if (match(altSubjectName)) {
return true;
}
}
}
} catch (SSLPeerUnverifiedException | CertificateParsingException e) {
LOGGER.error("Unable to check remote host", e);
return false;
}
return false;
}
private List<String> getAltSubjectNames(X509Certificate x509Certificate) {
List<String> subjectNames = new ArrayList<>();
try {
for (List<?> subjectAlternativeNames : x509Certificate.getSubjectAlternativeNames()) {
if (subjectAlternativeNames == null
|| subjectAlternativeNames.size() < 2
|| subjectAlternativeNames.get(0) == null
|| subjectAlternativeNames.get(1) == null) {
continue;
}
String subjectType = subjectAlternativeNames.get(0).toString();
switch (subjectType) {
case ALT_DNS_NAME_TYPE:
case ALT_IP_ADDRESS_TYPE:
subjectNames.add(subjectAlternativeNames.get(1).toString());
break;
default:
LOGGER.trace("Unusable subject type: " + subjectType);
}
}
} catch (CertificateParsingException e) {
LOGGER.error("Unable to parse the certificate", e);
return Collections.emptyList();
}
return subjectNames;
}
private boolean match(String altSubjectName) {
if (altSubjectName.startsWith("*.")) {
return altSubjectName.toLowerCase().endsWith(matrixHostname.toLowerCase());
} else {
return matrixHostname.equalsIgnoreCase(altSubjectName);
}
}
}
}

View File

@@ -45,7 +45,7 @@ import io.kamax.mxisd.lookup.ThreePidMapping;
import io.kamax.mxisd.lookup.strategy.LookupStrategy;
import io.kamax.mxisd.util.RestClientUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;

View File

@@ -27,7 +27,7 @@ import io.kamax.mxisd.config.ExecConfig;
import io.kamax.mxisd.profile.JsonProfileRequest;
import io.kamax.mxisd.profile.JsonProfileResult;
import io.kamax.mxisd.profile.ProfileProvider;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.StringUtils;
import java.util.Collections;
import java.util.List;

View File

@@ -1,168 +1,81 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2017 Kamax Sarl
*
* https://www.kamax.io/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.mxisd.backend.firebase;
import com.google.firebase.auth.UserInfo;
import com.google.i18n.phonenumbers.NumberParseException;
import com.google.i18n.phonenumbers.PhoneNumberUtil;
import io.kamax.matrix.ThreePid;
import io.kamax.matrix.ThreePidMedium;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseAuthException;
import com.google.firebase.auth.FirebaseToken;
import com.google.firebase.auth.UserRecord;
import io.kamax.matrix._MatrixID;
import io.kamax.mxisd.UserIdType;
import io.kamax.mxisd.auth.provider.AuthenticatorProvider;
import io.kamax.mxisd.auth.provider.BackendAuthResult;
import io.kamax.mxisd.config.FirebaseConfig;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
public class GoogleFirebaseAuthenticator extends GoogleFirebaseBackend implements AuthenticatorProvider {
public class GoogleFirebaseAuthenticator implements AuthenticatorProvider {
private transient final Logger log = LoggerFactory.getLogger(GoogleFirebaseAuthenticator.class);
private static final Logger log = LoggerFactory.getLogger(GoogleFirebaseAuthenticator.class);
private static final ExecutorService executor = Executors.newCachedThreadPool(); // Consider using a fixed thread pool or other strategies based on your app's needs
private PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
private FirebaseConfig config;
public GoogleFirebaseAuthenticator(FirebaseConfig cfg) {
this(cfg.isEnabled(), cfg.getCredentials(), cfg.getDatabase());
public GoogleFirebaseAuthenticator(FirebaseConfig config) {
this.config = config;
}
public GoogleFirebaseAuthenticator(boolean isEnabled, String credsPath, String db) {
super(isEnabled, "AuthenticationProvider", credsPath, db);
}
private void waitOnLatch(BackendAuthResult result, CountDownLatch l, String purpose) {
try {
l.await(30, TimeUnit.SECONDS);
} catch (InterruptedException e) {
log.warn("Interrupted while waiting for " + purpose);
result.fail();
}
}
private void toEmail(BackendAuthResult result, String email) {
if (StringUtils.isBlank(email)) {
return;
}
result.withThreePid(new ThreePid(ThreePidMedium.Email.getId(), email));
}
private void toMsisdn(BackendAuthResult result, String phoneNumber) {
if (StringUtils.isBlank(phoneNumber)) {
return;
}
try {
String number = phoneUtil.format(
phoneUtil.parse(
phoneNumber,
null // No default region
),
PhoneNumberUtil.PhoneNumberFormat.E164
).substring(1); // We want without the leading +
result.withThreePid(new ThreePid(ThreePidMedium.PhoneNumber.getId(), number));
} catch (NumberParseException e) {
log.warn("Invalid phone number: {}", phoneNumber);
}
}
private void waitOnLatch(CountDownLatch l) {
try {
l.await(30, TimeUnit.SECONDS);
} catch (InterruptedException e) {
log.warn("Interrupted while waiting for Firebase auth check");
}
@Override
public boolean isEnabled() {
return this.config.isEnabled();
}
@Override
public BackendAuthResult authenticate(_MatrixID mxid, String password) {
if (!isEnabled()) {
throw new IllegalStateException();
log.warn("Firebase authenticator is disabled.");
return BackendAuthResult.failure();
}
log.info("Trying to authenticate {}", mxid);
final BackendAuthResult result = BackendAuthResult.failure();
String localpart = mxid.getLocalPart();
CountDownLatch l = new CountDownLatch(1);
getFirebase().verifyIdToken(password).addOnSuccessListener(token -> {
CompletableFuture<BackendAuthResult> resultFuture = new CompletableFuture<>();
executor.submit(() -> {
try {
if (!StringUtils.equals(localpart, token.getUid())) {
log.info("Failure to authenticate {}: Matrix ID localpart '{}' does not match Firebase UID '{}'", mxid, localpart, token.getUid());
result.fail();
FirebaseToken decodedToken = FirebaseAuth.getInstance().verifyIdToken(password);
if (!mxid.getLocalPart().equals(decodedToken.getUid())) {
log.warn("UID mismatch for user {}", mxid);
resultFuture.complete(BackendAuthResult.failure());
return;
}
result.succeed(mxid.getId(), UserIdType.MatrixID.getId(), token.getName());
log.info("{} was successfully authenticated", mxid);
log.info("Fetching profile for {}", mxid);
CountDownLatch userRecordLatch = new CountDownLatch(1);
getFirebase().getUser(token.getUid()).addOnSuccessListener(user -> {
try {
toEmail(result, user.getEmail());
toMsisdn(result, user.getPhoneNumber());
for (UserInfo info : user.getProviderData()) {
toEmail(result, info.getEmail());
toMsisdn(result, info.getPhoneNumber());
}
log.info("Got {} 3PIDs in profile", result.getProfile().getThreePids().size());
} finally {
userRecordLatch.countDown();
}
}).addOnFailureListener(e -> {
try {
log.warn("Unable to fetch Firebase user profile for {}", mxid);
result.fail();
} finally {
userRecordLatch.countDown();
}
});
waitOnLatch(result, userRecordLatch, "Firebase user profile");
} finally {
l.countDown();
}
}).addOnFailureListener(e -> {
try {
if (e instanceof IllegalArgumentException) {
log.info("Failure to authenticate {}: invalid firebase token", mxid);
} else {
log.info("Failure to authenticate {}: {}", mxid, e.getMessage(), e);
log.info("Exception", e);
}
result.fail();
} finally {
l.countDown();
// Assuming you have a method to convert Firebase user info into BackendAuthResult
resultFuture.complete(convertToAuthResult(decodedToken));
} catch (FirebaseAuthException e) {
log.error("Failed to authenticate user {}: {}", mxid, e.getMessage(), e);
resultFuture.complete(BackendAuthResult.failure());
}
});
waitOnLatch(result, l, "Firebase auth check");
return result;
try {
return resultFuture.get(); // This will block, consider using thenAccept or similar for a truly non-blocking approach
} catch (Exception e) {
log.error("Error during authentication process", e);
return BackendAuthResult.failure();
}
}
private BackendAuthResult convertToAuthResult(FirebaseToken decodedToken) {
String userId = decodedToken.getUid(); // UID from Firebase as the user ID
String userIdType = "MatrixID"; // Assuming you're using string literals for user ID types
String displayName = decodedToken.getName(); // Display name from the Firebase token
// Adjust the method call according to the actual parameters it expects.
// This example uses three strings directly.
return BackendAuthResult.success(userId, userIdType, displayName);
}
// Ensure resources are properly released when no longer needed
public static void shutdown() {
executor.shutdown();
}
}

View File

@@ -1,90 +1,43 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2017 Kamax Sarl
*
* https://www.kamax.io/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.mxisd.backend.firebase;
import com.google.auth.oauth2.GoogleCredentials;
import com.google.firebase.FirebaseApp;
import com.google.firebase.FirebaseOptions;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseCredential;
import com.google.firebase.auth.FirebaseCredentials;
import com.google.firebase.database.FirebaseDatabase;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.FileInputStream;
import java.io.IOException;
public class GoogleFirebaseBackend {
public abstract class GoogleFirebaseBackend {
protected boolean enabled;
protected String backendName;
protected String credentialsPath;
protected String databaseUrl;
private transient final Logger log = LoggerFactory.getLogger(GoogleFirebaseBackend.class);
private boolean isEnabled;
private FirebaseAuth fbAuth;
protected FirebaseDatabase fbDb;
GoogleFirebaseBackend(boolean isEnabled, String name, String credsPath, String db) {
this.isEnabled = isEnabled;
if (!isEnabled) {
return;
}
try {
FirebaseApp fbApp = FirebaseApp.initializeApp(getOpts(credsPath, db), name);
fbAuth = FirebaseAuth.getInstance(fbApp);
FirebaseDatabase.getInstance(fbApp);
log.info("Google Firebase Authentication is ready");
} catch (IOException e) {
throw new RuntimeException("Error when initializing Firebase", e);
}
}
private FirebaseCredential getCreds(String credsPath) throws IOException {
if (StringUtils.isNotBlank(credsPath)) {
try (FileInputStream is = new FileInputStream(credsPath)) {
return FirebaseCredentials.fromCertificate(is);
public GoogleFirebaseBackend(boolean isEnabled, String backendName, String credsPath, String db) {
this.enabled = isEnabled;
this.backendName = backendName;
this.credentialsPath = credsPath;
this.databaseUrl = db;
if (isEnabled) {
try {
initializeFirebase();
} catch (IOException e) {
throw new RuntimeException("Failed to initialize Firebase", e);
}
} else {
return FirebaseCredentials.applicationDefault();
}
}
private FirebaseOptions getOpts(String credsPath, String db) throws IOException {
if (StringUtils.isBlank(db)) {
throw new IllegalArgumentException("Firebase database is not configured");
}
private void initializeFirebase() throws IOException {
FileInputStream serviceAccount = new FileInputStream(credentialsPath);
return new FirebaseOptions.Builder()
.setCredential(getCreds(credsPath))
.setDatabaseUrl(db)
FirebaseOptions options = new FirebaseOptions.Builder()
.setCredentials(GoogleCredentials.fromStream(serviceAccount))
.setDatabaseUrl(databaseUrl)
.build();
if (FirebaseApp.getApps().isEmpty()) { // Check if Firebase has been initialized already
FirebaseApp.initializeApp(options);
}
}
FirebaseAuth getFirebase() {
return fbAuth;
}
public boolean isEnabled() {
return isEnabled;
}
// Additional methods for GoogleFirebaseBackend
}

View File

@@ -1,28 +1,5 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2017 Kamax Sarl
*
* https://www.kamax.io/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.mxisd.backend.firebase;
import com.google.firebase.auth.UserRecord;
import com.google.firebase.tasks.OnFailureListener;
import com.google.firebase.tasks.OnSuccessListener;
import io.kamax.matrix.MatrixID;
import io.kamax.matrix.ThreePidMedium;
import io.kamax.mxisd.config.MxisdConfig;
@@ -36,25 +13,22 @@ import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.CompletableFuture;
public class GoogleFirebaseProvider extends GoogleFirebaseBackend implements IThreePidProvider {
private transient final Logger log = LoggerFactory.getLogger(GoogleFirebaseProvider.class);
private final Logger log = LoggerFactory.getLogger(GoogleFirebaseProvider.class);
private String domain;
public GoogleFirebaseProvider(MxisdConfig cfg) {
this(cfg.getFirebase().isEnabled(), cfg.getFirebase().getCredentials(), cfg.getFirebase().getDatabase(), cfg.getMatrix().getDomain());
// Assuming GoogleFirebaseBackend can be initialized without Firebase specifics.
super(cfg.getFirebase().isEnabled(), cfg.getFirebase().getCredentials(), cfg.getFirebase().getDatabase(), cfg.getMatrix().getDomain());
this.domain = cfg.getMatrix().getDomain();
}
public GoogleFirebaseProvider(boolean isEnabled, String credsPath, String db, String domain) {
super(isEnabled, "ThreePidProvider", credsPath, db);
this.domain = domain;
}
private String getMxid(UserRecord record) {
return MatrixID.asAcceptable(record.getUid(), domain).getId();
private String getMxid(String uid) {
// Mock UID to MXID conversion
return MatrixID.asAcceptable(uid, domain).getId();
}
@Override
@@ -67,71 +41,34 @@ public class GoogleFirebaseProvider extends GoogleFirebaseBackend implements ITh
return 25;
}
private void waitOnLatch(CountDownLatch l) {
try {
l.await(30, TimeUnit.SECONDS);
} catch (InterruptedException e) {
log.warn("Interrupted while waiting for Firebase auth check");
}
}
private Optional<String> findInternal(String medium, String address) {
CompletableFuture<Optional<String>> future = new CompletableFuture<>();
private Optional<UserRecord> findInternal(String medium, String address) {
final UserRecord[] r = new UserRecord[1];
CountDownLatch l = new CountDownLatch(1);
// Directly complete with empty to simulate no user found
future.complete(Optional.empty());
OnSuccessListener<UserRecord> success = result -> {
log.info("Found 3PID match for {}:{} - UID is {}", medium, address, result.getUid());
r[0] = result;
l.countDown();
};
OnFailureListener failure = e -> {
log.info("No 3PID match for {}:{} - {}", medium, address, e.getMessage());
r[0] = null;
l.countDown();
};
if (ThreePidMedium.Email.is(medium)) {
log.info("Performing E-mail 3PID lookup for {}", address);
getFirebase().getUserByEmail(address)
.addOnSuccessListener(success)
.addOnFailureListener(failure);
waitOnLatch(l);
} else if (ThreePidMedium.PhoneNumber.is(medium)) {
log.info("Performing msisdn 3PID lookup for {}", address);
getFirebase().getUserByPhoneNumber(address)
.addOnSuccessListener(success)
.addOnFailureListener(failure);
waitOnLatch(l);
} else {
log.info("{} is not a supported 3PID medium", medium);
r[0] = null;
}
return Optional.ofNullable(r[0]);
return future.join(); // Using join to avoid handling InterruptedException
}
@Override
public Optional<SingleLookupReply> find(SingleLookupRequest request) {
Optional<UserRecord> urOpt = findInternal(request.getType(), request.getThreePid());
return urOpt.map(userRecord -> new SingleLookupReply(request, getMxid(userRecord)));
Optional<String> uidOpt = findInternal(request.getType(), request.getThreePid());
return uidOpt.map(uid -> new SingleLookupReply(request, getMxid(uid)));
}
@Override
public List<ThreePidMapping> populate(List<ThreePidMapping> mappings) {
List<ThreePidMapping> results = new ArrayList<>();
mappings.parallelStream().forEach(o -> {
Optional<UserRecord> urOpt = findInternal(o.getMedium(), o.getValue());
if (urOpt.isPresent()) {
mappings.forEach(o -> {
Optional<String> uidOpt = findInternal(o.getMedium(), o.getValue());
uidOpt.ifPresent(uid -> {
ThreePidMapping result = new ThreePidMapping();
result.setMedium(o.getMedium());
result.setValue(o.getValue());
result.setMxid(getMxid(urOpt.get()));
result.setMxid(getMxid(uid));
results.add(result);
}
});
});
return results;
}
}

View File

@@ -32,7 +32,7 @@ import io.kamax.mxisd.config.MatrixConfig;
import io.kamax.mxisd.config.ldap.LdapConfig;
import io.kamax.mxisd.exception.InternalServerError;
import io.kamax.mxisd.util.GsonUtil;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.directory.api.ldap.model.cursor.CursorException;
import org.apache.directory.api.ldap.model.cursor.CursorLdapReferralException;
import org.apache.directory.api.ldap.model.cursor.EntryCursor;
@@ -54,6 +54,8 @@ public class LdapAuthProvider extends LdapBackend implements AuthenticatorProvid
private transient final Logger log = LoggerFactory.getLogger(LdapAuthProvider.class);
public static final char[] CHARACTERS_TO_ESCAPE = ",#+<>;\"=*\\\\".toCharArray();
private PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
public LdapAuthProvider(LdapConfig cfg, MatrixConfig mxCfg) {
@@ -94,7 +96,8 @@ public class LdapAuthProvider extends LdapBackend implements AuthenticatorProvid
return BackendAuthResult.failure();
}
String userFilter = "(" + getUidAtt() + "=" + userFilterValue + ")";
String filteredValue = escape(userFilterValue);
String userFilter = "(" + getUidAtt() + "=" + filteredValue + ")";
userFilter = buildWithFilter(userFilter, getCfg().getAuth().getFilter());
Set<String> attributes = new HashSet<>();
@@ -162,8 +165,21 @@ public class LdapAuthProvider extends LdapBackend implements AuthenticatorProvid
log.info("No match were found for {}", mxid);
return BackendAuthResult.failure();
} catch (LdapException | IOException | CursorException e) {
log.error("Unable to invoke query request: ", e);
throw new InternalServerError(e);
}
}
private String escape(String raw) {
StringBuilder sb = new StringBuilder();
boolean escape;
for (char c : raw.toCharArray()) {
escape = false;
for (int i = 0; i < CHARACTERS_TO_ESCAPE.length && !escape; i++) {
escape = CHARACTERS_TO_ESCAPE[i] == c;
}
sb.append(escape ? "\\" + c : c);
}
return sb.toString();
}
}

View File

@@ -20,10 +20,11 @@
package io.kamax.mxisd.backend.ldap;
import io.kamax.matrix.MatrixID;
import io.kamax.matrix._MatrixID;
import io.kamax.mxisd.config.MatrixConfig;
import io.kamax.mxisd.config.ldap.LdapConfig;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.directory.api.ldap.model.entry.Attribute;
import org.apache.directory.api.ldap.model.entry.AttributeUtils;
import org.apache.directory.api.ldap.model.entry.Entry;
@@ -116,10 +117,20 @@ public abstract class LdapBackend {
public String buildMatrixIdFromUid(String uid) {
String uidType = getCfg().getAttribute().getUid().getType();
String localpart = uid.toLowerCase();
if (!StringUtils.equals(uid, localpart)) {
log.info("UID {} from LDAP has been changed to lowercase to match the Synapse specifications", uid);
}
if (StringUtils.equals(UID, uidType)) {
return "@" + uid + ":" + mxCfg.getDomain();
if(getCfg().isActiveDirectory()) {
localpart = new UPN(uid.toLowerCase()).getMXID();
}
return "@" + localpart + ":" + mxCfg.getDomain();
} else if (StringUtils.equals(MATRIX_ID, uidType)) {
return uid;
return localpart;
} else {
throw new IllegalArgumentException("Bind type " + uidType + " is not supported");
}
@@ -128,6 +139,10 @@ public abstract class LdapBackend {
public String buildUidFromMatrixId(_MatrixID mxId) {
String uidType = getCfg().getAttribute().getUid().getType();
if (StringUtils.equals(UID, uidType)) {
if(getCfg().isActiveDirectory()) {
return new UPN(mxId).getUPN();
}
return mxId.getLocalPart();
} else if (StringUtils.equals(MATRIX_ID, uidType)) {
return mxId.getId();
@@ -169,4 +184,58 @@ public abstract class LdapBackend {
return values;
}
private class UPN {
private String login;
private String domain;
public UPN(String userPrincipalName) {
String[] uidParts = userPrincipalName.split("@");
if (uidParts.length != 2) {
throw new IllegalArgumentException(String.format("Wrong userPrincipalName provided: %s", userPrincipalName));
}
this.login = uidParts[0];
this.domain = uidParts[1];
}
public UPN(_MatrixID mxid) {
String[] idParts = mxid.getLocalPart().split("/");
if (idParts.length != 2) {
if(idParts.length == 1 && !StringUtils.isEmpty(getCfg().getDefaultDomain())) {
throw new IllegalArgumentException(String.format(
"Local part of mxid %s does not contains domain separator and default domain is not configured",
mxid.getLocalPart()
));
}
this.domain = getCfg().getDefaultDomain();
} else {
this.domain = idParts[1];
}
this.login = idParts[0];
}
public String getLogin() {
return login;
}
public String getDomain() {
return domain;
}
public String getMXID() {
if(StringUtils.equalsIgnoreCase(getCfg().getDefaultDomain(), this.domain)) {
return this.login;
}
return new StringBuilder(this.login).append("/").append(this.domain).toString();
}
public String getUPN() {
return new StringBuilder(this.login).append("@").append(this.domain).toString();
}
}
}

View File

@@ -41,7 +41,10 @@ import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
public class LdapThreePidProvider extends LdapBackend implements IThreePidProvider {
@@ -137,4 +140,65 @@ public class LdapThreePidProvider extends LdapBackend implements IThreePidProvid
return mappingsFound;
}
private List<String> getAttributes() {
final List<String> attributes = getCfg().getAttribute().getThreepid().values().stream().flatMap(List::stream)
.collect(Collectors.toList());
attributes.add(getUidAtt());
return attributes;
}
private Optional<String> getAttributeValue(Entry entry, List<String> attributes) {
return attributes.stream()
.map(attr -> getAttribute(entry, attr))
.filter(Objects::nonNull)
.filter(Optional::isPresent)
.map(Optional::get)
.findFirst();
}
@Override
public Iterable<ThreePidMapping> populateHashes() {
List<ThreePidMapping> result = new ArrayList<>();
if (!getCfg().getIdentity().isLookup()) {
return result;
}
String filter = getCfg().getIdentity().getFilter();
try (LdapConnection conn = getConn()) {
bind(conn);
log.debug("Query: {}", filter);
List<String> attributes = getAttributes();
log.debug("Attributes: {}", GsonUtil.build().toJson(attributes));
for (String baseDN : getBaseDNs()) {
log.debug("Base DN: {}", baseDN);
try (EntryCursor cursor = conn.search(baseDN, filter, SearchScope.SUBTREE, attributes.toArray(new String[0]))) {
while (cursor.next()) {
Entry entry = cursor.get();
log.info("Found possible match, DN: {}", entry.getDn().getName());
Optional<String> mxid = getAttribute(entry, getUidAtt());
if (!mxid.isPresent()) {
continue;
}
for (Map.Entry<String, List<String>> attributeEntry : getCfg().getAttribute().getThreepid().entrySet()) {
String medium = attributeEntry.getKey();
getAttributeValue(entry, attributeEntry.getValue())
.ifPresent(s -> result.add(new ThreePidMapping(medium, s, buildMatrixIdFromUid(mxid.get()))));
}
}
} catch (CursorLdapReferralException e) {
log.warn("3PID is only available via referral, skipping", e);
} catch (IOException | LdapException | CursorException e) {
log.error("Unable to fetch 3PID mappings", e);
}
}
} catch (LdapException | IOException e) {
log.error("Unable to fetch 3PID mappings", e);
}
return result;
}
}

View File

@@ -38,7 +38,7 @@ import io.kamax.mxisd.lookup.SingleLookupRequest;
import io.kamax.mxisd.lookup.ThreePidMapping;
import io.kamax.mxisd.lookup.provider.IThreePidProvider;
import io.kamax.mxisd.profile.ProfileProvider;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

View File

@@ -27,7 +27,7 @@ import io.kamax.mxisd.lookup.SingleLookupReply;
import io.kamax.mxisd.lookup.SingleLookupRequest;
import io.kamax.mxisd.lookup.ThreePidMapping;
import io.kamax.mxisd.lookup.provider.IThreePidProvider;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -107,14 +107,16 @@ public abstract class SqlThreePidProvider implements IThreePidProvider {
@Override
public Iterable<ThreePidMapping> populateHashes() {
if (StringUtils.isBlank(cfg.getLookup().getQuery())) {
String query = cfg.getLookup().getQuery();
if (StringUtils.isBlank(query)) {
log.warn("Lookup query not configured, skip.");
return Collections.emptyList();
}
log.debug("Uses query to match users: {}", query);
List<ThreePidMapping> result = new ArrayList<>();
try (Connection connection = pool.get()) {
PreparedStatement statement = connection.prepareStatement(cfg.getLookup().getQuery());
PreparedStatement statement = connection.prepareStatement(query);
try (ResultSet resultSet = statement.executeQuery()) {
while (resultSet.next()) {
String mxid = resultSet.getString("mxid");

View File

@@ -28,7 +28,7 @@ import io.kamax.mxisd.config.sql.generic.GenericSqlProviderConfig;
import io.kamax.mxisd.directory.DirectoryProvider;
import io.kamax.mxisd.exception.InternalServerError;
import io.kamax.mxisd.http.io.UserDirectorySearchResult;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

View File

@@ -29,23 +29,27 @@ import java.util.Optional;
public class Synapse {
private SqlConnectionPool pool;
private final SqlConnectionPool pool;
private final SynapseSqlProviderConfig providerConfig;
public Synapse(SynapseSqlProviderConfig sqlCfg) {
this.pool = new SqlConnectionPool(sqlCfg);
providerConfig = sqlCfg;
}
public Optional<String> getRoomName(String id) {
return pool.withConnFunction(conn -> {
PreparedStatement stmt = conn.prepareStatement(SynapseQueries.getRoomName());
stmt.setString(1, id);
ResultSet rSet = stmt.executeQuery();
if (!rSet.next()) {
return Optional.empty();
}
String query = providerConfig.isLegacyRoomNames() ? SynapseQueries.getLegacyRoomName() : SynapseQueries.getRoomName();
return Optional.ofNullable(rSet.getString(1));
return pool.withConnFunction(conn -> {
try (PreparedStatement stmt = conn.prepareStatement(query)) {
stmt.setString(1, id);
ResultSet rSet = stmt.executeQuery();
if (!rSet.next()) {
return Optional.empty();
}
return Optional.ofNullable(rSet.getString(1));
}
});
}
}

View File

@@ -21,7 +21,7 @@
package io.kamax.mxisd.backend.sql.synapse;
import io.kamax.mxisd.exception.ConfigurationException;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.StringUtils;
public class SynapseQueries {
@@ -51,7 +51,7 @@ public class SynapseQueries {
if (StringUtils.equals("sqlite", type)) {
return "select " + getUserId(type, domain) + ", displayname from profiles p where displayname like ?";
} else if (StringUtils.equals("postgresql", type)) {
return "select " + getUserId(type, domain) + ", displayname from profiles p where displayname ilike ?";
return "SELECT u.name,p.displayname FROM users u JOIN profiles p ON u.name LIKE concat('@',p.user_id,':%') WHERE u.is_guest = 0 AND u.appservice_id IS NULL AND p.displayname LIKE ?";
} else {
throw new ConfigurationException("Invalid Synapse SQL type: " + type);
}
@@ -72,7 +72,10 @@ public class SynapseQueries {
}
public static String getRoomName() {
return "select r.name from room_names r, events e, (select r1.room_id,max(e1.origin_server_ts) ts from room_names r1, events e1 where r1.event_id = e1.event_id group by r1.room_id) rle where e.origin_server_ts = rle.ts and r.event_id = e.event_id and r.room_id = ?";
return "select name from room_stats_state where room_id = ? limit 1";
}
public static String getLegacyRoomName() {
return "select r.name from room_names r, events e, (select r1.room_id,max(e1.origin_server_ts) ts from room_names r1, events e1 where r1.event_id = e1.event_id group by r1.room_id) rle where e.origin_server_ts = rle.ts and r.event_id = e.event_id and r.room_id = ?";
}
}

View File

@@ -25,7 +25,7 @@ import io.kamax.matrix._MatrixID;
import io.kamax.mxisd.UserIdType;
import io.kamax.mxisd.auth.provider.AuthenticatorProvider;
import io.kamax.mxisd.auth.provider.BackendAuthResult;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

View File

@@ -25,7 +25,7 @@ import io.kamax.matrix.json.GsonUtil;
import io.kamax.matrix.json.InvalidJsonException;
import io.kamax.mxisd.config.wordpress.WordpressConfig;
import io.kamax.mxisd.util.RestClientUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;

View File

@@ -0,0 +1,5 @@
package io.kamax.mxisd.config;
public interface DatabaseStorageConfig {
String getDatabase();
}

View File

@@ -5,6 +5,7 @@ import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
public class HashingConfig {
@@ -13,18 +14,19 @@ public class HashingConfig {
private boolean enabled = false;
private int pepperLength = 20;
private RotationPolicyEnum rotationPolicy;
private HashStorageEnum hashStorageType;
private HashStorageEnum hashStorageType = HashStorageEnum.in_memory;
private String delay = "10s";
private transient long delayInSeconds = 10;
private int requests = 10;
private List<Algorithm> algorithms = new ArrayList<>();
public void build() {
public void build(MatrixConfig matrixConfig) {
if (isEnabled()) {
LOGGER.info("--- Hash configuration ---");
LOGGER.info(" Pepper length: {}", getPepperLength());
LOGGER.info(" Rotation policy: {}", getRotationPolicy());
LOGGER.info(" Hash storage type: {}", getHashStorageType());
Objects.requireNonNull(getHashStorageType(), "Storage type must be specified");
if (RotationPolicyEnum.per_seconds == getRotationPolicy()) {
setDelayInSeconds(new DurationDeserializer().deserialize(getDelay()));
LOGGER.info(" Rotation delay: {}", getDelay());
@@ -35,6 +37,9 @@ public class HashingConfig {
}
LOGGER.info(" Algorithms: {}", getAlgorithms());
} else {
if (matrixConfig.isV2()) {
LOGGER.warn("V2 enabled without the hash configuration.");
}
LOGGER.info("Hash configuration disabled, used only `none` pepper.");
}
}

View File

@@ -0,0 +1,24 @@
package io.kamax.mxisd.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class InternalAPIConfig {
private final static Logger log = LoggerFactory.getLogger(InternalAPIConfig.class);
private boolean enabled = false;
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public void build() {
log.info("--- Internal API config ---");
log.info("Internal API enabled: {}", isEnabled());
}
}

View File

@@ -67,6 +67,7 @@ public class InvitationConfig {
private boolean recursive = true;
private long timer = 5;
private PeriodDimension period = PeriodDimension.minutes;
public boolean isRecursive() {
return recursive;
@@ -84,6 +85,13 @@ public class InvitationConfig {
this.timer = timer;
}
public PeriodDimension getPeriod() {
return period;
}
public void setPeriod(PeriodDimension period) {
this.period = period;
}
}
public static class SenderPolicy {
@@ -115,6 +123,7 @@ public class InvitationConfig {
private Expiration expiration = new Expiration();
private Resolution resolution = new Resolution();
private Policies policy = new Policies();
private boolean fullDisplayName = false;
public Expiration getExpiration() {
return expiration;
@@ -140,11 +149,26 @@ public class InvitationConfig {
this.policy = policy;
}
public boolean isFullDisplayName() {
return fullDisplayName;
}
public void setFullDisplayName(boolean fullDisplayName) {
this.fullDisplayName = fullDisplayName;
}
public void build() {
log.info("--- Invite config ---");
log.info("Expiration: {}", GsonUtil.get().toJson(getExpiration()));
log.info("Resolution: {}", GsonUtil.get().toJson(getResolution()));
log.info("Policies: {}", GsonUtil.get().toJson(getPolicy()));
log.info("Print full display name on invitation: {}", isFullDisplayName());
}
public enum PeriodDimension {
minutes,
seconds
}
}

View File

@@ -21,7 +21,7 @@
package io.kamax.mxisd.config;
import io.kamax.mxisd.exception.ConfigurationException;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.StringUtils;
public class KeyConfig {

View File

@@ -0,0 +1,60 @@
package io.kamax.mxisd.config;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LoggingConfig {
private static final Logger LOGGER = LoggerFactory.getLogger("App");
private String root;
private String app;
private boolean requests = false;
public String getRoot() {
return root;
}
public void setRoot(String root) {
this.root = root;
}
public String getApp() {
return app;
}
public void setApp(String app) {
this.app = app;
}
public boolean isRequests() {
return requests;
}
public void setRequests(boolean requests) {
this.requests = requests;
}
public void build() {
LOGGER.info("Logging config:");
if (StringUtils.isNotBlank(getRoot())) {
LOGGER.info(" Default log level: {}", getRoot());
System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", getRoot());
}
String appLevel = System.getProperty("org.slf4j.simpleLogger.log.io.kamax.mxisd");
if (StringUtils.isNotBlank(appLevel)) {
LOGGER.info(" Logging level set by environment: {}", appLevel);
} else if (StringUtils.isNotBlank(getApp())) {
System.setProperty("org.slf4j.simpleLogger.log.io.kamax.mxisd", getApp());
LOGGER.info(" Logging level set by the configuration: {}", getApp());
} else {
LOGGER.info(" Logging level hasn't set, use default");
}
LOGGER.info(" Log requests: {}", isRequests());
if (isRequests()) {
LOGGER.warn(" Request dumping enabled, use this only to debug purposes, don't use it in the production.");
}
}
}

View File

@@ -22,7 +22,7 @@ package io.kamax.mxisd.config;
import io.kamax.matrix.json.GsonUtil;
import io.kamax.mxisd.exception.ConfigurationException;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -64,7 +64,7 @@ public class MatrixConfig {
private String domain;
private Identity identity = new Identity();
private boolean v1 = true;
private boolean v2 = true;
private boolean v2 = false;
public String getDomain() {
return domain;

View File

@@ -29,7 +29,7 @@ import io.kamax.mxisd.config.sql.synapse.SynapseSqlProviderConfig;
import io.kamax.mxisd.config.threepid.ThreePidConfig;
import io.kamax.mxisd.config.threepid.notification.NotificationConfig;
import io.kamax.mxisd.config.wordpress.WordpressConfig;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -117,6 +117,8 @@ public class MxisdConfig {
private WordpressConfig wordpress = new WordpressConfig();
private PolicyConfig policy = new PolicyConfig();
private HashingConfig hashing = new HashingConfig();
private LoggingConfig logging = new LoggingConfig();
private InternalAPIConfig internal = new InternalAPIConfig();
public AppServiceConfig getAppsvc() {
return appsvc;
@@ -342,6 +344,14 @@ public class MxisdConfig {
this.hashing = hashing;
}
public LoggingConfig getLogging() {
return logging;
}
public void setLogging(LoggingConfig logging) {
this.logging = logging;
}
public MxisdConfig inMemory() {
getKey().setPath(":memory:");
getStorage().getProvider().getSqlite().setDatabase(":memory:");
@@ -349,7 +359,17 @@ public class MxisdConfig {
return this;
}
public InternalAPIConfig getInternal() {
return internal;
}
public void setInternal(InternalAPIConfig internal) {
this.internal = internal;
}
public MxisdConfig build() {
getLogging().build();
if (StringUtils.isBlank(getServer().getName())) {
getServer().setName(getMatrix().getDomain());
log.debug("server.name is empty, using matrix.domain");
@@ -359,6 +379,7 @@ public class MxisdConfig {
getAuth().build();
getAccountConfig().build();
getDirectory().build();
getDns().build();
getExec().build();
getFirebase().build();
getForward().build();
@@ -381,7 +402,8 @@ public class MxisdConfig {
getView().build();
getWordpress().build();
getPolicy().build();
getHashing().build();
getHashing().build(getMatrix());
getInternal().build();
return this;
}

View File

@@ -0,0 +1,105 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2017 Kamax Sarl
*
* https://www.kamax.io/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.mxisd.config;
public class PostgresqlStorageConfig implements DatabaseStorageConfig {
private String database;
private String username;
private String password;
private boolean pool;
private int maxConnectionsFree = 1;
private long maxConnectionAgeMillis = 60 * 60 * 1000;
private long checkConnectionsEveryMillis = 30 * 1000;
private boolean testBeforeGetFromPool = false;
@Override
public String getDatabase() {
return database;
}
public void setDatabase(String database) {
this.database = database;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public boolean isPool() {
return pool;
}
public void setPool(boolean pool) {
this.pool = pool;
}
public int getMaxConnectionsFree() {
return maxConnectionsFree;
}
public void setMaxConnectionsFree(int maxConnectionsFree) {
this.maxConnectionsFree = maxConnectionsFree;
}
public long getMaxConnectionAgeMillis() {
return maxConnectionAgeMillis;
}
public void setMaxConnectionAgeMillis(long maxConnectionAgeMillis) {
this.maxConnectionAgeMillis = maxConnectionAgeMillis;
}
public long getCheckConnectionsEveryMillis() {
return checkConnectionsEveryMillis;
}
public void setCheckConnectionsEveryMillis(long checkConnectionsEveryMillis) {
this.checkConnectionsEveryMillis = checkConnectionsEveryMillis;
}
public boolean isTestBeforeGetFromPool() {
return testBeforeGetFromPool;
}
public void setTestBeforeGetFromPool(boolean testBeforeGetFromPool) {
this.testBeforeGetFromPool = testBeforeGetFromPool;
}
}

View File

@@ -20,10 +20,11 @@
package io.kamax.mxisd.config;
public class SQLiteStorageConfig {
public class SQLiteStorageConfig implements DatabaseStorageConfig {
private String database;
@Override
public String getDatabase() {
return database;
}

View File

@@ -20,7 +20,7 @@
package io.kamax.mxisd.config;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -34,6 +34,7 @@ public class ServerConfig {
private String name;
private int port = 8090;
private String publicUrl;
private String hostname;
public String getName() {
return name;
@@ -59,6 +60,14 @@ public class ServerConfig {
this.publicUrl = publicUrl;
}
public String getHostname() {
return hostname;
}
public void setHostname(String hostname) {
this.hostname = hostname;
}
public void build() {
log.info("--- Server config ---");
@@ -75,8 +84,13 @@ public class ServerConfig {
log.warn("Public URL is not valid: {}", StringUtils.defaultIfBlank(e.getMessage(), "<no reason provided>"));
}
if (StringUtils.isBlank(getHostname())) {
setHostname("0.0.0.0");
}
log.info("Name: {}", getName());
log.info("Port: {}", getPort());
log.info("Public URL: {}", getPublicUrl());
log.info("Hostname: {}", getHostname());
}
}

View File

@@ -21,14 +21,21 @@
package io.kamax.mxisd.config;
import io.kamax.mxisd.exception.ConfigurationException;
import org.apache.commons.lang.StringUtils;
public class StorageConfig {
public enum BackendEnum {
sqlite,
postgresql
}
public static class Provider {
private SQLiteStorageConfig sqlite = new SQLiteStorageConfig();
private PostgresqlStorageConfig postgresql = new PostgresqlStorageConfig();
public SQLiteStorageConfig getSqlite() {
return sqlite;
}
@@ -37,16 +44,23 @@ public class StorageConfig {
this.sqlite = sqlite;
}
public PostgresqlStorageConfig getPostgresql() {
return postgresql;
}
public void setPostgresql(PostgresqlStorageConfig postgresql) {
this.postgresql = postgresql;
}
}
private String backend = "sqlite";
private BackendEnum backend = BackendEnum.sqlite; // or postgresql
private Provider provider = new Provider();
public String getBackend() {
public BackendEnum getBackend() {
return backend;
}
public void setBackend(String backend) {
public void setBackend(BackendEnum backend) {
this.backend = backend;
}
@@ -59,7 +73,7 @@ public class StorageConfig {
}
public void build() {
if (StringUtils.isBlank(getBackend())) {
if (getBackend() == null) {
throw new ConfigurationException("storage.backend");
}
}

View File

@@ -21,7 +21,7 @@
package io.kamax.mxisd.config;
import io.kamax.matrix.json.GsonUtil;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

View File

@@ -1,79 +1,57 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2018 Kamax Sàrl
*
* https://www.kamax.io/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.mxisd.config;
import io.kamax.matrix.json.GsonUtil;
import io.kamax.mxisd.exception.ConfigurationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.Constructor;
import org.yaml.snakeyaml.introspector.BeanAccess;
import org.yaml.snakeyaml.parser.ParserException;
import org.yaml.snakeyaml.representer.Representer;
import org.yaml.snakeyaml.LoaderOptions;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.File;
import java.util.Optional;
public class YamlConfigLoader {
private static final Logger log = LoggerFactory.getLogger(YamlConfigLoader.class);
public static MxisdConfig loadFromFile(String path) throws IOException {
File f = new File(path).getAbsoluteFile();
log.info("Reading config from {}", f.toString());
Representer rep = new Representer();
rep.getPropertyUtils().setBeanAccess(BeanAccess.FIELD);
rep.getPropertyUtils().setAllowReadOnlyProperties(true);
rep.getPropertyUtils().setSkipMissingProperties(true);
Yaml yaml = new Yaml(new Constructor(MxisdConfig.class), rep);
try (FileInputStream is = new FileInputStream(f)) {
MxisdConfig raw = yaml.load(is);
log.debug("Read config in memory from {}", path);
public static MxisdConfig loadFromFile(String path) throws IOException {
File file = new File(path); // Define the file from the path
Constructor constructor = new Constructor(MxisdConfig.class); // Ensure correct import
Yaml yaml = new Yaml(constructor); // No change needed here, this is correct
// SnakeYaml set objects to null when there is no value set in the config, even a full sub-tree.
// This is problematic for default config values and objects, to avoid NPEs.
// Therefore, we'll use Gson to re-parse the data in a way that avoids us checking the whole config for nulls.
MxisdConfig cfg = GsonUtil.get().fromJson(GsonUtil.get().toJson(raw), MxisdConfig.class);
log.info("Loaded config from {}", path);
return cfg;
} catch (ParserException t) {
throw new ConfigurationException(t.getMessage(), "Could not parse YAML config file - Please check indentation and that the configuration options exist");
}
// Load from YAML
try (FileInputStream inputStream = new FileInputStream(file)) {
return yaml.loadAs(inputStream, MxisdConfig.class);
} catch (IOException e) {
// Handle exceptions
throw e;
}
}
public static Optional<MxisdConfig> tryLoadFromFile(String path) {
log.debug("Attempting to read config from {}", path);
try {
return Optional.of(loadFromFile(path));
} catch (FileNotFoundException e) {
log.info("No config file at {}", path);
return Optional.empty();
} catch (IOException e) {
throw new RuntimeException(e);
log.warn("Unable to load configuration file from path {}: {}", path, e.getMessage());
return Optional.empty();
}
}
public static void dumpConfig(MxisdConfig cfg, String outputPath) throws IOException {
// Initialize LoaderOptions for dumping if needed
LoaderOptions loaderOptions = new LoaderOptions();
// Customize loaderOptions as necessary
Yaml yaml = new Yaml(loaderOptions);
try (FileWriter writer = new FileWriter(new File(outputPath))) {
yaml.dump(cfg, writer);
log.info("Configuration dumped successfully to {}", outputPath);
} catch (IOException e) {
log.error("Failed to dump YAML configuration to path: {}", outputPath, e);
throw e;
}
}
}

View File

@@ -24,7 +24,7 @@ import io.kamax.matrix.ThreePidMedium;
import io.kamax.matrix.json.GsonUtil;
import io.kamax.mxisd.backend.ldap.LdapBackend;
import io.kamax.mxisd.exception.ConfigurationException;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -233,6 +233,7 @@ public abstract class LdapConfig {
private String filter;
private String token = "%3pid";
private Map<String, String> medium = new HashMap<>();
private boolean lookup = false;
public String getFilter() {
return filter;
@@ -262,6 +263,13 @@ public abstract class LdapConfig {
this.medium = medium;
}
public boolean isLookup() {
return lookup;
}
public void setLookup(boolean lookup) {
this.lookup = lookup;
}
}
public static class Profile {
@@ -283,6 +291,9 @@ public abstract class LdapConfig {
private boolean enabled;
private String filter;
private boolean activeDirectory;
private String defaultDomain;
private Connection connection = new Connection();
private Attribute attribute = new Attribute();
private Auth auth = new Auth();
@@ -308,6 +319,22 @@ public abstract class LdapConfig {
this.filter = filter;
}
public boolean isActiveDirectory() {
return activeDirectory;
}
public void setActiveDirectory(boolean activeDirectory) {
this.activeDirectory = activeDirectory;
}
public String getDefaultDomain() {
return defaultDomain;
}
public void setDefaultDomain(String defaultDomain) {
this.defaultDomain = defaultDomain;
}
public Connection getConnection() {
return connection;
}
@@ -399,6 +426,15 @@ public abstract class LdapConfig {
throw new ConfigurationException("ldap.identity.token");
}
if(isActiveDirectory()) {
if(!StringUtils.equals(LdapBackend.UID, uidType)) {
throw new IllegalArgumentException(String.format(
"Attribute UID type should be set to %s in Active Directory mode",
LdapBackend.UID
));
}
}
// Build queries
attribute.getThreepid().forEach((k, v) -> {
if (StringUtils.isBlank(identity.getMedium().get(k))) {

View File

@@ -45,4 +45,21 @@ public class MemoryThreePid implements _ThreePid {
this.address = address;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MemoryThreePid threePid = (MemoryThreePid) o;
if (!medium.equals(threePid.medium)) return false;
return address.equals(threePid.address);
}
@Override
public int hashCode() {
int result = medium.hashCode();
result = 31 * result + address.hashCode();
return result;
}
}

View File

@@ -21,7 +21,7 @@
package io.kamax.mxisd.config.rest;
import io.kamax.mxisd.exception.ConfigurationException;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -33,8 +33,8 @@ public class RestBackendConfig {
public static class IdentityEndpoints {
private String single = "/_ma1sd/backend/api/v1/identity/single";
private String bulk = "/_ma1sd/backend/api/v1/identity/bulk";
private String single = "/_mxids/backend/api/v1/identity/single";
private String bulk = "/_mxids/backend/api/v1/identity/bulk";
public String getSingle() {
return single;
@@ -56,9 +56,9 @@ public class RestBackendConfig {
public static class ProfileEndpoints {
private String displayName = "/_ma1sd/backend/api/v1/profile/displayName";
private String threepids = "/_ma1sd/backend/api/v1/profile/threepids";
private String roles = "/_ma1sd/backend/api/v1/profile/roles";
private String displayName = "/_mxids/backend/api/v1/profile/displayName";
private String threepids = "/_mxids/backend/api/v1/profile/threepids";
private String roles = "/_mxids/backend/api/v1/profile/roles";
public String getDisplayName() {
return displayName;
@@ -88,8 +88,8 @@ public class RestBackendConfig {
public static class Endpoints {
private String auth = "/_ma1sd/backend/api/v1/auth/login";
private String directory = "/_ma1sd/backend/api/v1/directory/user/search";
private String auth = "/_mxids/backend/api/v1/auth/login";
private String directory = "/_mxids/backend/api/v1/directory/user/search";
private IdentityEndpoints identity = new IdentityEndpoints();
private ProfileEndpoints profile = new ProfileEndpoints();

View File

@@ -125,7 +125,7 @@ public abstract class SqlConfig {
}
public static class Lookup {
private String query = "SELECT user_id AS mxid, medium, address from user_threepids";
private String query = "SELECT user_id AS mxid, medium, address from user_threepid_id_server";
public String getQuery() {
return query;
@@ -140,7 +140,7 @@ public abstract class SqlConfig {
private Boolean enabled;
private String type = "mxid";
private String query = "SELECT user_id AS uid FROM user_threepids WHERE medium = ? AND address = ?";
private String query = "SELECT user_id AS uid FROM user_threepid_id_server WHERE medium = ? AND address = ?";
private Map<String, String> medium = new HashMap<>();
public Boolean isEnabled() {

View File

@@ -23,10 +23,24 @@ package io.kamax.mxisd.config.sql.synapse;
import io.kamax.mxisd.UserIdType;
import io.kamax.mxisd.backend.sql.synapse.SynapseQueries;
import io.kamax.mxisd.config.sql.SqlConfig;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SynapseSqlProviderConfig extends SqlConfig {
private transient final Logger log = LoggerFactory.getLogger(SynapseSqlProviderConfig.class);
private boolean legacyRoomNames = false;
public boolean isLegacyRoomNames() {
return legacyRoomNames;
}
public void setLegacyRoomNames(boolean legacyRoomNames) {
this.legacyRoomNames = legacyRoomNames;
}
@Override
protected String getProviderName() {
return "Synapse SQL";
@@ -42,7 +56,7 @@ public class SynapseSqlProviderConfig extends SqlConfig {
if (getIdentity().isEnabled() && StringUtils.isBlank(getIdentity().getType())) {
getIdentity().setType("mxid");
getIdentity().setQuery("SELECT user_id AS uid FROM user_threepids WHERE medium = ? AND address = ?");
getIdentity().setQuery("SELECT user_id AS uid FROM user_threepid_id_server WHERE medium = ? AND address = ?");
}
if (getProfile().isEnabled()) {
@@ -65,4 +79,12 @@ public class SynapseSqlProviderConfig extends SqlConfig {
printConfig();
}
@Override
protected void printConfig() {
super.printConfig();
if (isEnabled()) {
log.info("Use legacy room name query: {}", isLegacyRoomNames());
}
}
}

View File

@@ -21,7 +21,7 @@
package io.kamax.mxisd.config.threepid.connector;
import io.kamax.mxisd.util.GsonUtil;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

View File

@@ -20,7 +20,7 @@
package io.kamax.mxisd.config.threepid.connector;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

View File

@@ -20,7 +20,7 @@
package io.kamax.mxisd.config.threepid.medium;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.StringUtils;
import java.util.HashMap;
import java.util.Map;

View File

@@ -21,7 +21,7 @@
package io.kamax.mxisd.config.wordpress;
import io.kamax.mxisd.exception.ConfigurationException;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.StringUtils;
import java.util.HashMap;
import java.util.Map;

View File

@@ -31,7 +31,7 @@ import io.kamax.mxisd.http.io.UserDirectorySearchRequest;
import io.kamax.mxisd.http.io.UserDirectorySearchResult;
import io.kamax.mxisd.util.RestClientUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;

View File

@@ -23,5 +23,6 @@ package io.kamax.mxisd.exception;
public class InvalidParamException extends RuntimeException {
public InvalidParamException() {
super("The chosen hash algorithm is invalid or disallowed");
}
}

View File

@@ -23,5 +23,6 @@ package io.kamax.mxisd.exception;
public class InvalidPepperException extends RuntimeException {
public InvalidPepperException() {
super("The provided pepper is invalid or expired");
}
}

View File

@@ -0,0 +1,28 @@
/*
* mxids - Matrix Identity Server Daemon
* Copyright (C) 2020 Anatoliy SAblin
*
* https://git.cqre.net/cqrenet/mxids/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.mxisd.exception;
public class TermsNotSignedException extends RuntimeException {
public TermsNotSignedException() {
super("Please accept our updated terms of service before continuing");
}
}

View File

@@ -1,6 +1,9 @@
package io.kamax.mxisd.hash;
import io.kamax.mxisd.config.HashingConfig;
import io.kamax.mxisd.hash.engine.Engine;
import io.kamax.mxisd.hash.engine.HashEngine;
import io.kamax.mxisd.hash.engine.NoneEngine;
import io.kamax.mxisd.hash.rotation.HashRotationStrategy;
import io.kamax.mxisd.hash.rotation.NoOpRotationStrategy;
import io.kamax.mxisd.hash.rotation.RotationPerRequests;
@@ -21,7 +24,7 @@ public class HashManager {
private static final Logger LOGGER = LoggerFactory.getLogger(HashManager.class);
private HashEngine hashEngine;
private Engine engine;
private HashRotationStrategy rotationStrategy;
private HashStorage hashStorage;
private HashingConfig config;
@@ -32,7 +35,7 @@ public class HashManager {
this.config = config;
this.storage = storage;
initStorage();
hashEngine = new HashEngine(providers, getHashStorage(), config);
engine = config.isEnabled() ? new HashEngine(providers, getHashStorage(), config) : new NoneEngine();
initRotationStrategy();
configured.set(true);
}
@@ -73,8 +76,8 @@ public class HashManager {
this.rotationStrategy.register(getHashEngine());
}
public HashEngine getHashEngine() {
return hashEngine;
public Engine getHashEngine() {
return engine;
}
public HashRotationStrategy getRotationStrategy() {

View File

@@ -0,0 +1,7 @@
package io.kamax.mxisd.hash.engine;
public interface Engine {
void updateHashes();
String getPepper();
}

View File

@@ -1,4 +1,4 @@
package io.kamax.mxisd.hash;
package io.kamax.mxisd.hash.engine;
import io.kamax.mxisd.config.HashingConfig;
import io.kamax.mxisd.hash.storage.HashStorage;
@@ -12,7 +12,7 @@ import org.slf4j.LoggerFactory;
import java.util.Base64;
import java.util.List;
public class HashEngine {
public class HashEngine implements Engine {
private static final Logger LOGGER = LoggerFactory.getLogger(HashEngine.class);
@@ -28,6 +28,7 @@ public class HashEngine {
this.config = config;
}
@Override
public void updateHashes() {
LOGGER.info("Start update hashes.");
synchronized (hashStorage) {
@@ -35,7 +36,9 @@ public class HashEngine {
hashStorage.clear();
for (IThreePidProvider provider : providers) {
try {
LOGGER.info("Populate hashes from the handler: {}", provider.getClass().getCanonicalName());
for (ThreePidMapping pidMapping : provider.populateHashes()) {
LOGGER.debug("Found 3PID: {}", pidMapping);
hashStorage.add(pidMapping, hash(pidMapping));
}
} catch (Exception e) {
@@ -46,6 +49,7 @@ public class HashEngine {
LOGGER.info("Finish update hashes.");
}
@Override
public String getPepper() {
synchronized (hashStorage) {
return pepper;

View File

@@ -0,0 +1,19 @@
package io.kamax.mxisd.hash.engine;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class NoneEngine implements Engine {
private static final Logger LOGGER = LoggerFactory.getLogger(NoneEngine.class);
@Override
public void updateHashes() {
LOGGER.info("Nothing to update.");
}
@Override
public String getPepper() {
return "";
}
}

View File

@@ -1,12 +1,12 @@
package io.kamax.mxisd.hash.rotation;
import io.kamax.mxisd.hash.HashEngine;
import io.kamax.mxisd.hash.engine.Engine;
public interface HashRotationStrategy {
void register(HashEngine hashEngine);
void register(Engine engine);
HashEngine getHashEngine();
Engine getHashEngine();
void newRequest();

View File

@@ -1,19 +1,19 @@
package io.kamax.mxisd.hash.rotation;
import io.kamax.mxisd.hash.HashEngine;
import io.kamax.mxisd.hash.engine.Engine;
public class NoOpRotationStrategy implements HashRotationStrategy {
private HashEngine hashEngine;
private Engine engine;
@Override
public void register(HashEngine hashEngine) {
this.hashEngine = hashEngine;
public void register(Engine engine) {
this.engine = engine;
}
@Override
public HashEngine getHashEngine() {
return hashEngine;
public Engine getHashEngine() {
return engine;
}
@Override

View File

@@ -1,12 +1,12 @@
package io.kamax.mxisd.hash.rotation;
import io.kamax.mxisd.hash.HashEngine;
import io.kamax.mxisd.hash.engine.Engine;
import java.util.concurrent.atomic.AtomicInteger;
public class RotationPerRequests implements HashRotationStrategy {
private HashEngine hashEngine;
private Engine engine;
private final AtomicInteger counter = new AtomicInteger(0);
private final int barrier;
@@ -15,14 +15,14 @@ public class RotationPerRequests implements HashRotationStrategy {
}
@Override
public void register(HashEngine hashEngine) {
this.hashEngine = hashEngine;
public void register(Engine engine) {
this.engine = engine;
trigger();
}
@Override
public HashEngine getHashEngine() {
return hashEngine;
public Engine getHashEngine() {
return engine;
}
@Override

View File

@@ -1,6 +1,6 @@
package io.kamax.mxisd.hash.rotation;
import io.kamax.mxisd.hash.HashEngine;
import io.kamax.mxisd.hash.engine.Engine;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
@@ -9,7 +9,7 @@ import java.util.concurrent.TimeUnit;
public class TimeBasedRotation implements HashRotationStrategy {
private final long delay;
private HashEngine hashEngine;
private Engine engine;
private final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
public TimeBasedRotation(long delay) {
@@ -17,15 +17,15 @@ public class TimeBasedRotation implements HashRotationStrategy {
}
@Override
public void register(HashEngine hashEngine) {
this.hashEngine = hashEngine;
public void register(Engine engine) {
this.engine = engine;
Runtime.getRuntime().addShutdownHook(new Thread(executorService::shutdown));
executorService.scheduleWithFixedDelay(this::trigger, 0, delay, TimeUnit.SECONDS);
}
@Override
public HashEngine getHashEngine() {
return hashEngine;
public Engine getHashEngine() {
return engine;
}
@Override

View File

@@ -20,11 +20,19 @@
package io.kamax.mxisd.http;
import static io.kamax.mxisd.util.RestClientUtils.urlEncode;
public class IsAPIv1 {
public static final String Base = "/_matrix/identity/api/v1";
public static String getValidate(String medium, String sid, String secret, String token) {
return String.format("%s/validate/%s/submitToken?sid=%s&client_secret=%s&token=%s", Base, medium, sid, secret, token);
return String.format("%s/validate/%s/submitToken?sid=%s&client_secret=%s&token=%s",
Base,
medium,
urlEncode(sid),
urlEncode(secret),
urlEncode(token)
);
}
}

View File

@@ -0,0 +1,5 @@
package io.kamax.mxisd.http.undertow.conduit;
public interface ConduitWithDump {
String dump();
}

View File

@@ -0,0 +1,107 @@
/*
* JBoss, Home of Professional Open Source.
* Copyright 2014 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* 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 io.kamax.mxisd.http.undertow.conduit;
import org.xnio.IoUtils;
import org.xnio.channels.StreamSourceChannel;
import org.xnio.conduits.AbstractStreamSinkConduit;
import org.xnio.conduits.ConduitWritableByteChannel;
import org.xnio.conduits.Conduits;
import org.xnio.conduits.StreamSinkConduit;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* Conduit that saves all the data that is written through it and can dump it to the console
* <p>
* Obviously this should not be used in production.
*
* @author Stuart Douglas
*/
public class DebuggingStreamSinkConduit extends AbstractStreamSinkConduit<StreamSinkConduit> implements ConduitWithDump {
private final List<byte[]> data = new CopyOnWriteArrayList<>();
/**
* Construct a new instance.
*
* @param next the delegate conduit to set
*/
public DebuggingStreamSinkConduit(StreamSinkConduit next) {
super(next);
}
@Override
public int write(ByteBuffer src) throws IOException {
int pos = src.position();
int res = super.write(src);
if (res > 0) {
byte[] d = new byte[res];
for (int i = 0; i < res; ++i) {
d[i] = src.get(i + pos);
}
data.add(d);
}
return res;
}
@Override
public long write(ByteBuffer[] dsts, int offs, int len) throws IOException {
for (int i = offs; i < len; ++i) {
if (dsts[i].hasRemaining()) {
return write(dsts[i]);
}
}
return 0;
}
@Override
public long transferFrom(final FileChannel src, final long position, final long count) throws IOException {
return src.transferTo(position, count, new ConduitWritableByteChannel(this));
}
@Override
public long transferFrom(final StreamSourceChannel source, final long count, final ByteBuffer throughBuffer) throws IOException {
return IoUtils.transfer(source, count, throughBuffer, new ConduitWritableByteChannel(this));
}
@Override
public int writeFinal(ByteBuffer src) throws IOException {
return Conduits.writeFinalBasic(this, src);
}
@Override
public long writeFinal(ByteBuffer[] srcs, int offset, int length) throws IOException {
return Conduits.writeFinalBasic(this, srcs, offset, length);
}
@Override
public String dump() {
StringBuilder sb = new StringBuilder();
for (byte[] datum : data) {
sb.append(new String(datum, StandardCharsets.UTF_8));
}
return sb.toString();
}
}

View File

@@ -0,0 +1,95 @@
/*
* JBoss, Home of Professional Open Source.
* Copyright 2014 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* 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 io.kamax.mxisd.http.undertow.conduit;
import org.xnio.IoUtils;
import org.xnio.channels.StreamSinkChannel;
import org.xnio.conduits.AbstractStreamSourceConduit;
import org.xnio.conduits.ConduitReadableByteChannel;
import org.xnio.conduits.StreamSourceConduit;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* Conduit that saves all the data that is written through it and can dump it to the console
* <p>
* Obviously this should not be used in production.
*
* @author Stuart Douglas
*/
public class DebuggingStreamSourceConduit extends AbstractStreamSourceConduit<StreamSourceConduit> implements ConduitWithDump {
private final List<byte[]> data = new CopyOnWriteArrayList<>();
/**
* Construct a new instance.
*
* @param next the delegate conduit to set
*/
public DebuggingStreamSourceConduit(StreamSourceConduit next) {
super(next);
}
public long transferTo(final long position, final long count, final FileChannel target) throws IOException {
return target.transferFrom(new ConduitReadableByteChannel(this), position, count);
}
public long transferTo(final long count, final ByteBuffer throughBuffer, final StreamSinkChannel target) throws IOException {
return IoUtils.transfer(new ConduitReadableByteChannel(this), count, throughBuffer, target);
}
@Override
public int read(ByteBuffer dst) throws IOException {
int pos = dst.position();
int res = super.read(dst);
if (res > 0) {
byte[] d = new byte[res];
for (int i = 0; i < res; ++i) {
d[i] = dst.get(i + pos);
}
data.add(d);
}
return res;
}
@Override
public long read(ByteBuffer[] dsts, int offs, int len) throws IOException {
for (int i = offs; i < len; ++i) {
if (dsts[i].hasRemaining()) {
return read(dsts[i]);
}
}
return 0;
}
@Override
public String dump() {
StringBuilder sb = new StringBuilder();
for (byte[] datum : data) {
sb.append(new String(datum, StandardCharsets.UTF_8));
}
return sb.toString();
}
}

View File

@@ -0,0 +1,23 @@
package io.kamax.mxisd.http.undertow.conduit;
import io.undertow.server.ConduitWrapper;
import io.undertow.server.HttpServerExchange;
import io.undertow.util.ConduitFactory;
import org.xnio.conduits.Conduit;
public abstract class LazyConduitWrapper<T extends Conduit> implements ConduitWrapper<T> {
private T conduit = null;
protected abstract T create(ConduitFactory<T> factory, HttpServerExchange exchange);
@Override
public T wrap(ConduitFactory<T> factory, HttpServerExchange exchange) {
conduit = create(factory, exchange);
return conduit;
}
public T get() {
return conduit;
}
}

View File

@@ -33,6 +33,7 @@ import io.kamax.mxisd.util.RestClientUtils;
import io.undertow.server.HttpHandler;
import io.undertow.server.HttpServerExchange;
import io.undertow.server.handlers.form.FormData;
import io.undertow.util.Headers;
import io.undertow.util.HttpString;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
@@ -189,7 +190,7 @@ public abstract class BasicHttpHandler implements HttpHandler {
}
protected void respond(HttpServerExchange ex, int status, String errCode, String error) {
respond(ex, status, buildErrorBody(ex, errCode, error));
respond(ex, status, buildErrorBody(ex, errCode, error != null ? error : "An error has occurred"));
}
protected void handleException(HttpServerExchange exchange, HttpMatrixException ex) {
@@ -203,26 +204,34 @@ public abstract class BasicHttpHandler implements HttpHandler {
}
protected void proxyPost(HttpServerExchange exchange, JsonObject body, CloseableHttpClient client, ClientDnsOverwrite dns) {
proxyPost(exchange, body, client, dns, false);
}
protected void proxyPost(HttpServerExchange exchange, JsonObject body, CloseableHttpClient client, ClientDnsOverwrite dns,
boolean defaultJsonResponse) {
String target = dns.transform(URI.create(exchange.getRequestURL())).toString();
log.info("Requesting remote: {}", target);
HttpPost req = RestClientUtils.post(target, GsonUtil.get(), body);
exchange.getRequestHeaders().forEach(header -> {
header.forEach(v -> {
String name = header.getHeaderName().toString();
if (!StringUtils.startsWithIgnoreCase(name, "content-")) {
req.addHeader(name, v);
}
});
});
exchange.getRequestHeaders().forEach(header -> header.forEach(v -> {
String name = header.getHeaderName().toString();
if (!StringUtils.startsWithIgnoreCase(name, "content-")) {
req.addHeader(name, v);
}
}));
boolean missingJsonResponse = true;
try (CloseableHttpResponse res = client.execute(req)) {
exchange.setStatusCode(res.getStatusLine().getStatusCode());
for (Header h : res.getAllHeaders()) {
for (HeaderElement el : h.getElements()) {
missingJsonResponse = !Headers.CONTENT_TYPE_STRING.equalsIgnoreCase(h.getName());
exchange.getResponseHeaders().add(HttpString.tryFromString(h.getName()), el.getValue());
}
}
if (defaultJsonResponse && missingJsonResponse) {
exchange.getRequestHeaders().add(Headers.CONTENT_TYPE, "application/json");
}
res.getEntity().writeTo(exchange.getOutputStream());
exchange.endExchange();
} catch (IOException e) {

View File

@@ -23,6 +23,7 @@ package io.kamax.mxisd.http.undertow.handler;
import io.kamax.mxisd.auth.AccountManager;
import io.kamax.mxisd.config.PolicyConfig;
import io.kamax.mxisd.exception.InvalidCredentialsException;
import io.kamax.mxisd.exception.TermsNotSignedException;
import io.undertow.server.HttpHandler;
import io.undertow.server.HttpServerExchange;
import org.slf4j.Logger;
@@ -66,7 +67,7 @@ public class CheckTermsHandler extends BasicHttpHandler {
if (!accountManager.isTermAccepted(token, policies)) {
log.error("Non accepting request from: {}", exchange.getHostAndPort());
throw new InvalidCredentialsException();
throw new TermsNotSignedException();
}
log.trace("Access granted");
child.handleRequest(exchange);

View File

@@ -0,0 +1,186 @@
/*
* JBoss, Home of Professional Open Source.
* Copyright 2014 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* 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 io.kamax.mxisd.http.undertow.handler;
import io.kamax.mxisd.http.undertow.conduit.ConduitWithDump;
import io.kamax.mxisd.http.undertow.conduit.DebuggingStreamSinkConduit;
import io.kamax.mxisd.http.undertow.conduit.DebuggingStreamSourceConduit;
import io.kamax.mxisd.http.undertow.conduit.LazyConduitWrapper;
import io.undertow.security.api.SecurityContext;
import io.undertow.server.HttpHandler;
import io.undertow.server.HttpServerExchange;
import io.undertow.server.handlers.Cookie;
import io.undertow.util.ConduitFactory;
import io.undertow.util.HeaderValues;
import io.undertow.util.Headers;
import io.undertow.util.LocaleUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xnio.conduits.StreamSinkConduit;
import org.xnio.conduits.StreamSourceConduit;
import java.util.Deque;
import java.util.Iterator;
import java.util.Map;
/**
* Handler that dumps a exchange to a log.
*
* @author Stuart Douglas
*/
public class RequestDumpingHandler implements HttpHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(RequestDumpingHandler.class);
private final HttpHandler next;
public RequestDumpingHandler(HttpHandler next) {
this.next = next;
}
@Override
public void handleRequest(HttpServerExchange exchange) throws Exception {
LazyConduitWrapper<StreamSourceConduit> requestConduitWrapper = new LazyConduitWrapper<StreamSourceConduit>() {
@Override
protected StreamSourceConduit create(ConduitFactory<StreamSourceConduit> factory, HttpServerExchange exchange) {
return new DebuggingStreamSourceConduit(factory.create());
}
};
LazyConduitWrapper<StreamSinkConduit> responseConduitWrapper = new LazyConduitWrapper<StreamSinkConduit>() {
@Override
protected StreamSinkConduit create(ConduitFactory<StreamSinkConduit> factory, HttpServerExchange exchange) {
return new DebuggingStreamSinkConduit(factory.create());
}
};
exchange.addRequestWrapper(requestConduitWrapper);
exchange.addResponseWrapper(responseConduitWrapper);
final StringBuilder sb = new StringBuilder();
// Log pre-service information
final SecurityContext sc = exchange.getSecurityContext();
sb.append("\n----------------------------REQUEST---------------------------\n");
sb.append(" URI=").append(exchange.getRequestURI()).append("\n");
sb.append(" characterEncoding=").append(exchange.getRequestHeaders().get(Headers.CONTENT_ENCODING)).append("\n");
sb.append(" contentLength=").append(exchange.getRequestContentLength()).append("\n");
sb.append(" contentType=").append(exchange.getRequestHeaders().get(Headers.CONTENT_TYPE)).append("\n");
//sb.append(" contextPath=" + exchange.getContextPath());
if (sc != null) {
if (sc.isAuthenticated()) {
sb.append(" authType=").append(sc.getMechanismName()).append("\n");
sb.append(" principle=").append(sc.getAuthenticatedAccount().getPrincipal()).append("\n");
} else {
sb.append(" authType=none\n");
}
}
Map<String, Cookie> cookies = exchange.getRequestCookies();
if (cookies != null) {
for (Map.Entry<String, Cookie> entry : cookies.entrySet()) {
Cookie cookie = entry.getValue();
sb.append(" cookie=").append(cookie.getName()).append("=").append(cookie.getValue()).append("\n");
}
}
for (HeaderValues header : exchange.getRequestHeaders()) {
for (String value : header) {
sb.append(" header=").append(header.getHeaderName()).append("=").append(value).append("\n");
}
}
sb.append(" locale=").append(LocaleUtils.getLocalesFromHeader(exchange.getRequestHeaders().get(Headers.ACCEPT_LANGUAGE)))
.append("\n");
sb.append(" method=").append(exchange.getRequestMethod()).append("\n");
Map<String, Deque<String>> pnames = exchange.getQueryParameters();
for (Map.Entry<String, Deque<String>> entry : pnames.entrySet()) {
String pname = entry.getKey();
Iterator<String> pvalues = entry.getValue().iterator();
sb.append(" parameter=");
sb.append(pname);
sb.append('=');
while (pvalues.hasNext()) {
sb.append(pvalues.next());
if (pvalues.hasNext()) {
sb.append(", ");
}
}
sb.append("\n");
}
//sb.append(" pathInfo=" + exchange.getPathInfo());
sb.append(" protocol=").append(exchange.getProtocol()).append("\n");
sb.append(" queryString=").append(exchange.getQueryString()).append("\n");
sb.append(" remoteAddr=").append(exchange.getSourceAddress()).append("\n");
sb.append(" remoteHost=").append(exchange.getSourceAddress().getHostName()).append("\n");
//sb.append("requestedSessionId=" + exchange.getRequestedSessionId());
sb.append(" scheme=").append(exchange.getRequestScheme()).append("\n");
sb.append(" host=").append(exchange.getRequestHeaders().getFirst(Headers.HOST)).append("\n");
sb.append(" serverPort=").append(exchange.getDestinationAddress().getPort()).append("\n");
//sb.append(" servletPath=" + exchange.getServletPath());
sb.append(" isSecure=").append(exchange.isSecure()).append("\n");
exchange.addExchangeCompleteListener((exchange1, nextListener) -> {
StreamSourceConduit sourceConduit = requestConduitWrapper.get();
if (sourceConduit instanceof ConduitWithDump) {
ConduitWithDump conduitWithDump = (ConduitWithDump) sourceConduit;
sb.append("body=\n");
sb.append(conduitWithDump.dump()).append("\n");
}
// Log post-service information
sb.append("--------------------------RESPONSE--------------------------\n");
if (sc != null) {
if (sc.isAuthenticated()) {
sb.append(" authType=").append(sc.getMechanismName()).append("\n");
sb.append(" principle=").append(sc.getAuthenticatedAccount().getPrincipal()).append("\n");
} else {
sb.append(" authType=none\n");
}
}
sb.append(" contentLength=").append(exchange1.getResponseContentLength()).append("\n");
sb.append(" contentType=").append(exchange1.getResponseHeaders().getFirst(Headers.CONTENT_TYPE)).append("\n");
Map<String, Cookie> cookies1 = exchange1.getResponseCookies();
if (cookies1 != null) {
for (Cookie cookie : cookies1.values()) {
sb.append(" cookie=").append(cookie.getName()).append("=").append(cookie.getValue()).append("; domain=")
.append(cookie.getDomain()).append("; path=").append(cookie.getPath()).append("\n");
}
}
for (HeaderValues header : exchange1.getResponseHeaders()) {
for (String value : header) {
sb.append(" header=").append(header.getHeaderName()).append("=").append(value).append("\n");
}
}
sb.append(" status=").append(exchange1.getStatusCode()).append("\n");
StreamSinkConduit streamSinkConduit = responseConduitWrapper.get();
if (streamSinkConduit instanceof ConduitWithDump) {
ConduitWithDump conduitWithDump = (ConduitWithDump) streamSinkConduit;
sb.append("body=\n");
sb.append(conduitWithDump.dump());
}
sb.append("\n==============================================================");
nextListener.proceed();
LOGGER.info(sb.toString());
});
// Perform the exchange
next.handleRequest(exchange);
}
}

View File

@@ -82,7 +82,10 @@ public class SaneHandler extends BasicHttpHandler {
} catch (InvalidJsonException e) {
respond(exchange, HttpStatus.SC_BAD_REQUEST, e.getErrorCode(), e.getError());
} catch (InvalidCredentialsException e) {
log.error("Unauthorized: ", e);
respond(exchange, HttpStatus.SC_UNAUTHORIZED, "M_UNAUTHORIZED", e.getMessage());
} catch (TermsNotSignedException e) {
respond(exchange, HttpStatus.SC_FORBIDDEN, "M_TERMS_NOT_SIGNED", e.getMessage());
} catch (ObjectNotFoundException e) {
respond(exchange, HttpStatus.SC_NOT_FOUND, "M_NOT_FOUND", e.getMessage());
} catch (NotImplementedException e) {

View File

@@ -29,10 +29,7 @@ import java.util.Optional;
public abstract class ApplicationServiceHandler extends BasicHttpHandler {
protected String getToken(HttpServerExchange ex) {
return Optional.ofNullable(ex.getQueryParameters()
.getOrDefault("access_token", new LinkedList<>())
.peekFirst()
).orElse("");
return getAccessToken(ex);
}
}

Some files were not shown because too many files have changed in this diff Show More