Compare commits

...

189 Commits
2.1.2 ... 0.0.1

Author SHA1 Message Date
4fd4fdac60 Updates to build sucessfully - breaking changes in Firebase and SendGrid 2024-04-02 15:42:15 +02: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
Anatoly Sablin
17ebc2a421 Fix hash generation. 2019-12-06 23:15:00 +03:00
Anatoly Sablin
cbb9fced8d Clarify the documentation. Add the hash config to the example config. Uses duration in the delay field instead of the seconds. 2019-12-05 23:27:13 +03:00
Anatoly Sablin
7509174611 Add documentation. Add options to enable/disable the hash providers. Add the option for setup barrier for rotation per requests strategy. 2019-12-02 23:23:17 +03:00
Anatoly Sablin
51d9225dda Don't wrap the AcceptTermsHandler with terms checking. Clear the sql hash storage on shutdown. 2019-12-02 22:31:36 +03:00
Anatoly Sablin
6216113400 FIx terms. 2019-11-29 23:38:52 +03:00
Anatoly Sablin
cb32441959 Fix sha256 hashing. Fix v2 lookup. 2019-11-29 00:26:08 +03:00
Anatoly Sablin
0ec4df2c06 Fix bug with token expiration. Increase the default length of the pepper. Update hashes on startup with RotationPerRequest strategy. Don't check for existing pepper on the none hash algorithm. 2019-11-28 00:28:11 +03:00
Anatoly Sablin
86b880069b Wrap with the CheckTermsHandler handlers only with authorization. 2019-11-27 22:55:34 +03:00
Anatoly Sablin
a97273fe77 Wrap with the CheckTermsHandler is necessary. 2019-11-25 23:35:56 +03:00
Anatoly Sablin
f9daf4d58a Make configuration enums in lowercase. Wrap create hashes by try-catch. Add initial part of the documentation. 2019-11-15 23:39:45 +03:00
Anatoly Sablin
9e4cabb69b Fix the token expiration period. 2019-11-15 22:50:08 +03:00
Anatoly Sablin
0b81de3cd0 Make the federation homeserver resolve more accurate (on resolve via DNS record check that the certificate present for the original host). 2019-11-13 23:08:34 +03:00
Anatoly Sablin
698a16ec17 Fix matrix server hostname verification. 2019-11-11 23:48:49 +03:00
Anatoly Sablin
619b70d860 Bump gradle to 6.0. 2019-11-11 23:48:49 +03:00
Anatoly Sablin
494c9e3941 Merge branch 'MSC2140'
# Conflicts:
#	src/main/java/io/kamax/mxisd/session/SessionManager.java
2019-11-07 22:29:25 +03:00
Anatoly Sablin
0786a6520f Bump gradle version. 2019-11-07 00:16:18 +03:00
Anatoly Sablin
430136c391 Bump dependency verions. 2019-11-06 23:26:56 +03:00
Anatoly Sablin
eda4404335 MSC2140 Add populating hashes via exec identity store. 2019-11-06 23:16:27 +03:00
Anatoly Sablin
c52034b18a MSC2140 Add populating hashes via sql and memory stores. 2019-11-06 23:07:42 +03:00
Anatoly Sablin
8d346037b7 MSC2140 Add hash configuration. 2019-11-06 00:20:39 +03:00
Anatoly Sablin
14ad4435bc MSC2140 Add SQL storage for hashes and the time-based rotation policy. 2019-11-05 23:18:11 +03:00
Anatoly Sablin
8ba8756871 Fix account registration. 2019-10-21 23:48:47 +03:00
Anatoly Sablin
43fe8b1aec Add the hash lookup handler. 2019-10-18 22:52:13 +03:00
Anatoly Sablin
a0270c7d01 Add NoOp configuration. Split classes into packages. 2019-10-16 23:07:14 +03:00
Anatoly Sablin
703044d06a Add initial Hash configuration. Add the HashDetailsHandler. 2019-10-15 23:38:32 +03:00
Anatoly Sablin
add6ed8fd9 Add the TOS API. 2019-10-09 23:12:23 +03:00
Anatoly Sablin
baed894ff8 Update policy configuration. Add Handler to check that user accepts terms. 2019-10-08 00:13:40 +03:00
Anatoly Sablin
14e095a147 Add policy configuration. 2019-10-04 00:03:51 +03:00
Anatoly Sablin
bc8795e940 Add authorization handler. 2019-10-01 23:52:01 +03:00
Anatoly Sablin
5521c0c338 Add account handlers. 2019-09-30 23:53:38 +03:00
Anatoly Sablin
614b3440e2 Registration API. Add DAO, Manager. 2019-09-30 23:16:58 +03:00
Anatoly Sablin
d0fd9fb9b0 Get domain from public url. 2019-09-17 23:17:30 +03:00
Anatoly Sablin
1232e9ce79 MSC2140 MSC2134 Remove the unused path. 2019-09-01 22:38:59 +03:00
Anatoly Sablin
a47a983c10 MSC2140 MSC2134 Refactoring. Move common classes to the share package. 2019-09-01 22:33:03 +03:00
Anatoly Sablin
fbb0d7c7ba MSC2140 Mirroring V1 to V2. 2019-08-31 23:35:58 +03:00
Anatoly Sablin
f1dd309551 MSC2140 Add option to enable/disable v1 and v2 api. 2019-08-31 23:09:20 +03:00
185 changed files with 5304 additions and 1169 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/ .idea/
# Local dev config # Local dev config
/ma1sd.yaml /mxids.yaml
/application.yaml /application.yaml
# Local dev storage # Local dev storage
/ma1sd.db /mxids.db

View File

@@ -1,18 +1,26 @@
FROM --platform=$BUILDPLATFORM openjdk:8-jre-alpine AS builder
RUN apk update && apk add gradle git && rm -rf /var/lib/apk/* /var/cache/apk/*
WORKDIR /mxids
COPY . .
RUN ./gradlew shadowJar
FROM openjdk:8-jre-alpine FROM openjdk:8-jre-alpine
RUN apk update && apk add bash && rm -rf /var/lib/apk/* /var/cache/apk/* RUN apk update && apk add bash && rm -rf /var/lib/apk/* /var/cache/apk/*
VOLUME /etc/ma1sd VOLUME /etc/mxids
VOLUME /var/ma1sd VOLUME /var/mxids
EXPOSE 8090 EXPOSE 8090
ENV JAVA_OPTS="" ENV JAVA_OPTS=""
ENV CONF_FILE_PATH="/etc/ma1sd/ma1sd.yaml" ENV CONF_FILE_PATH="/etc/mxids/mxids.yaml"
ENV SIGN_KEY_PATH="/var/ma1sd/sign.key" ENV SIGN_KEY_PATH="/var/mxids/sign.key"
ENV SQLITE_DATABASE_PATH="/var/ma1sd/ma1sd.db" ENV SQLITE_DATABASE_PATH="/var/mxids/mxids.db"
CMD [ "/start.sh" ] CMD [ "/start.sh" ]
ADD src/docker/start.sh /start.sh ADD src/docker/start.sh /start.sh
ADD src/script/ma1sd /app/ma1sd ADD src/script/mxids /app/mxids
ADD build/libs/ma1sd.jar /app/ma1sd.jar COPY --from=builder /mxids/build/libs/mxids.jar /app/mxids.jar

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 * Copyright (C) 2017 Kamax Sarl
* *
* https://www.kamax.io/ * https://www.kamax.io/
@@ -20,22 +20,22 @@
import java.util.regex.Pattern import java.util.regex.Pattern
apply plugin: 'java' apply plugin: 'java-library'
apply plugin: 'application' apply plugin: 'application'
apply plugin: 'com.github.johnrengelman.shadow' apply plugin: 'com.github.johnrengelman.shadow'
apply plugin: 'idea' apply plugin: 'idea'
apply plugin: 'com.github.ben-manes.versions' apply plugin: 'com.github.ben-manes.versions'
def confFileName = "ma1sd.example.yaml" def confFileName = "mxids.example.yaml"
def distDir = "${project.buildDir}/dist" def distDir = "${project.buildDir}/dist"
def debBinPath = "/usr/lib/ma1sd" def debBinPath = "/usr/lib/mxids"
def debConfPath = "/etc/ma1sd" def debConfPath = "/etc/mxids"
def debDataPath = "/var/lib/ma1sd" def debDataPath = "/var/lib/mxids"
def debSystemdPath = "/etc/systemd/system" def debSystemdPath = "/etc/systemd/system"
def debConfFileName = confFileName def debConfFileName = confFileName
def debStartScriptFilename = "ma1sd" def debStartScriptFilename = "mxids"
def debBuildBasePath = "${project.buildDir}/tmp/debian" def debBuildBasePath = "${project.buildDir}/tmp/debian"
def debBuildDebianPath = "${debBuildBasePath}/DEBIAN" def debBuildDebianPath = "${debBuildBasePath}/DEBIAN"
@@ -44,18 +44,18 @@ def debBuildConfPath = "${debBuildBasePath}${debConfPath}"
def debBuildDataPath = "${debBuildBasePath}${debDataPath}" def debBuildDataPath = "${debBuildBasePath}${debDataPath}"
def debBuildSystemdPath = "${debBuildBasePath}${debSystemdPath}" def debBuildSystemdPath = "${debBuildBasePath}${debSystemdPath}"
def dockerImageName = "ma1uta/ma1sd" def dockerImageName = "cqrenet/mxids"
def dockerImageTag = "${dockerImageName}:${ma1sdVersion()}" def dockerImageTag = "${dockerImageName}:${mxidsVersion()}"
group = 'io.kamax' group = 'io.kamax'
mainClassName = 'io.kamax.mxisd.MxisdStandaloneExec' mainClassName = 'io.kamax.mxisd.MxisdStandaloneExec'
sourceCompatibility = '1.8' sourceCompatibility = '11'
targetCompatibility = '1.8' targetCompatibility = '11'
String ma1sdVersion() { String mxidsVersion() {
def versionPattern = Pattern.compile("v(\\d+\\.)?(\\d+\\.)?(\\d+)(-.*)?") 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) { if (version == null || version.size() == 0) {
version = gitVersion() version = gitVersion()
} }
@@ -73,109 +73,114 @@ String gitVersion() {
buildscript { buildscript {
repositories { repositories {
jcenter() gradlePluginPortal()
mavenCentral()
google()
} }
dependencies { dependencies {
classpath 'com.github.jengelman.gradle.plugins:shadow:5.1.0' classpath 'com.github.johnrengelman:shadow:8.1.1'
classpath 'com.github.ben-manes:gradle-versions-plugin:0.21.0' classpath 'com.github.ben-manes:gradle-versions-plugin:0.51.0'
} }
} }
repositories { repositories {
jcenter() mavenCentral()
} }
dependencies { dependencies {
// Logging // Logging
compile 'org.slf4j:slf4j-simple:1.7.25' api 'org.slf4j:slf4j-simple:2.0.12'
// Easy file management // Easy file management
compile 'commons-io:commons-io:2.6' api 'commons-io:commons-io:2.15.1'
// Config management // Config management
compile 'org.yaml:snakeyaml:1.24' api 'org.yaml:snakeyaml:1.33'
// Dependencies from old Matrix-java-sdk // Dependencies from old Matrix-java-sdk
compile 'org.apache.commons:commons-lang3:3.9' api 'org.apache.commons:commons-lang3:3.14.0'
compile 'com.squareup.okhttp3:okhttp:4.0.1' api 'com.squareup.okhttp3:okhttp:4.12.0'
compile 'commons-codec:commons-codec:1.12' api 'commons-codec:commons-codec:1.16.1'
// ORMLite // ORMLite
compile 'com.j256.ormlite:ormlite-jdbc:5.1' api 'com.j256.ormlite:ormlite-jdbc:6.1'
// ed25519 handling // ed25519 handling
compile 'net.i2p.crypto:eddsa:0.3.0' api 'net.i2p.crypto:eddsa:0.3.0'
// LDAP connector // LDAP connector
compile 'org.apache.directory.api:api-all:1.0.0' api 'org.apache.directory.api:api-all:2.1.6'
// DNS lookups // DNS lookups
compile 'dnsjava:dnsjava:2.1.9' api 'dnsjava:dnsjava:3.5.3'
// HTTP connections // HTTP connections
compile 'org.apache.httpcomponents:httpclient:4.5.9' api 'org.apache.httpcomponents:httpclient:4.5.14'
// Phone numbers validation // Phone numbers validation
compile 'com.googlecode.libphonenumber:libphonenumber:8.10.15' api 'com.googlecode.libphonenumber:libphonenumber:8.13.33'
// E-mail sending // E-mail sending
compile 'javax.mail:javax.mail-api:1.6.2' api 'javax.mail:javax.mail-api:1.6.2'
compile 'com.sun.mail:javax.mail:1.6.2' api 'com.sun.mail:javax.mail:1.6.2'
// Google Firebase Authentication backend // Google Firebase Authentication backend
compile 'com.google.firebase:firebase-admin:5.3.0' api 'com.google.firebase:firebase-admin:9.2.0'
// Connection Pool // Connection Pool
compile 'com.mchange:c3p0:0.9.5.4' api 'com.mchange:c3p0:0.10.0'
// SQLite // SQLite
compile 'org.xerial:sqlite-jdbc:3.28.0' api 'org.xerial:sqlite-jdbc:3.45.2.0'
// PostgreSQL // PostgreSQL
compile 'org.postgresql:postgresql:42.2.6' api 'org.postgresql:postgresql:42.7.3'
// MariaDB/MySQL // MariaDB/MySQL
compile 'org.mariadb.jdbc:mariadb-java-client:2.4.2' api 'org.mariadb.jdbc:mariadb-java-client:3.3.3'
// UNIX sockets
api 'com.kohlschutter.junixsocket:junixsocket-core:2.9.0'
// Twilio SDK for SMS // Twilio SDK for SMS
compile 'com.twilio.sdk:twilio:7.40.1' api 'com.twilio.sdk:twilio:10.1.2'
// SendGrid SDK to send emails from GCE // 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 // ZT-Exec for exec identity store
compile 'org.zeroturnaround:zt-exec:1.11' api 'org.zeroturnaround:zt-exec:1.12'
// HTTP server // HTTP server
compile 'io.undertow:undertow-core:2.0.22.Final' api 'io.undertow:undertow-core:2.3.12.Final'
// Command parser for AS interface // Command parser for AS interface
implementation 'commons-cli:commons-cli:1.4' api 'commons-cli:commons-cli:1.6.0'
testCompile 'junit:junit:4.13-beta-3' testImplementation 'junit:junit:4.13.2'
testCompile 'com.github.tomakehurst:wiremock:2.24.0' testImplementation 'com.github.tomakehurst:wiremock:3.0.1'
testCompile 'com.unboundid:unboundid-ldapsdk:4.0.11' testImplementation 'com.unboundid:unboundid-ldapsdk:7.0.0'
testCompile 'com.icegreen:greenmail:1.5.10' testImplementation 'com.icegreen:greenmail:1.6.15'
} }
jar { jar {
manifest { manifest {
attributes( attributes(
'Implementation-Version': ma1sdVersion() 'Implementation-Version': mxidsVersion()
) )
} }
} }
shadowJar { shadowJar {
baseName = project.name archiveBaseName.set(project.name)
classifier = null archiveClassifier.set('') // Set to an empty string if you don't need a classifier.
version = null archiveVersion.set('') // Set to an empty string if you don't want the version in the jar name.
} }
task debBuild(dependsOn: shadowJar) { task debBuild(dependsOn: shadowJar) {
doLast { doLast {
String debVersion = ma1sdVersion() String debVersion = mxidsVersion()
println "Version for package: ${debVersion}" println "Version for package: ${debVersion}"
mkdir distDir mkdir distDir
mkdir debBuildBasePath mkdir debBuildBasePath
@@ -186,7 +191,7 @@ task debBuild(dependsOn: shadowJar) {
mkdir debBuildSystemdPath mkdir debBuildSystemdPath
copy { copy {
from "${project.buildDir}/libs/ma1sd.jar" from "${project.buildDir}/libs/mxids.jar"
into debBuildBinPath into debBuildBinPath
} }
@@ -234,12 +239,12 @@ task debBuild(dependsOn: shadowJar) {
ant.replace( ant.replace(
file: "${debBuildDebianPath}/postinst", file: "${debBuildDebianPath}/postinst",
token: '%DEB_CONF_FILE%', token: '%DEB_CONF_FILE%',
value: "${debConfPath}/ma1sd.yaml" value: "${debConfPath}/mxids.yaml"
) )
ant.chmod( ant.chmod(
file: "${debBuildDebianPath}/postinst", file: "${debBuildDebianPath}/postinst",
perm: 'a+x' perm: '0755'
) )
ant.chmod( ant.chmod(
@@ -248,7 +253,7 @@ task debBuild(dependsOn: shadowJar) {
) )
copy { copy {
from "${project.file('src/systemd/ma1sd.service')}" from "${project.file('src/systemd/mxids.service')}"
into debBuildSystemdPath 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 commandLine 'docker', 'build', '-t', dockerImageTag, project.rootDir
doLast { 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) { task dockerPush(type: Exec) {
commandLine 'docker', 'push', dockerImageTag 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
}

146
docs/MSC2140_MSC2134.md Normal file
View File

@@ -0,0 +1,146 @@
# MSC2140
## V1 vs V2
In the [MSC2140](https://github.com/matrix-org/matrix-doc/pull/2140) the v2 prefix was introduced.
Default values:
```.yaml
matrix:
v1: true # deprecated
v2: false
```
To disable change value to `false`.
NOTE: the v1 is deprecated, therefore recommend to use only v2 and disable v1 (default value can be ommited):
```.yaml
matrix:
v1: false
```
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:
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'
```
Where:
- `term_name` -- name of the terms.
- `version` -- the terms version.
- `lang` -- the term language.
- `name` -- the name 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.
There are the next API:
- [`GET /_matrix/identity/v2/account`](https://matrix.org/docs/spec/identity_service/r0.3.0#get-matrix-identity-v2-account) - Gets information about what user owns the access token used in the request.
- [`POST /_matrix/identity/v2/account/logout`](https://matrix.org/docs/spec/identity_service/r0.3.0#post-matrix-identity-v2-account-logout) - Logs out the access token, preventing it from being used to authenticate future requests to the server.
- [`GET /_matrix/identity/v2/hash_details`](https://matrix.org/docs/spec/identity_service/r0.3.0#get-matrix-identity-v2-hash-details) - Gets parameters for hashing identifiers from the server. This can include any of the algorithms defined in this specification.
- [`POST /_matrix/identity/v2/lookup`](https://matrix.org/docs/spec/identity_service/r0.3.0#post-matrix-identity-v2-lookup) - Looks up the set of Matrix User IDs which have bound the 3PIDs given, if bindings are available. Note that the format of the addresses is defined later in this specification.
- [`POST /_matrix/identity/v2/validate/email/requestToken`](https://matrix.org/docs/spec/identity_service/r0.3.0#post-matrix-identity-v2-validate-email-requesttoken) - Create a session for validating an email address.
- [`POST /_matrix/identity/v2/validate/email/submitToken`](https://matrix.org/docs/spec/identity_service/r0.3.0#post-matrix-identity-v2-validate-email-submittoken) - Validate ownership of an email address.
- [`GET /_matrix/identity/v2/validate/email/submitToken`](https://matrix.org/docs/spec/identity_service/r0.3.0#get-matrix-identity-v2-validate-email-submittoken) - Validate ownership of an email address.
- [`POST /_matrix/identity/v2/validate/msisdn/requestToken`](https://matrix.org/docs/spec/identity_service/r0.3.0#post-matrix-identity-v2-validate-msisdn-requesttoken) - Create a session for validating a phone number.
- [`POST /_matrix/identity/v2/validate/msisdn/submitToken`](https://matrix.org/docs/spec/identity_service/r0.3.0#post-matrix-identity-v2-validate-msisdn-submittoken) - Validate ownership of a phone number.
- [`GET /_matrix/identity/v2/validate/msisdn/submitToken`](https://matrix.org/docs/spec/identity_service/r0.3.0#get-matrix-identity-v2-validate-msisdn-submittoken) - Validate ownership of a phone number.
- [`GET /_matrix/identity/v2/3pid/getValidated3pid`](https://matrix.org/docs/spec/identity_service/r0.3.0#get-matrix-identity-v2-3pid-getvalidated3pid) - Determines if a given 3pid has been validated by a user.
- [`POST /_matrix/identity/v2/3pid/bind`](https://matrix.org/docs/spec/identity_service/r0.3.0#post-matrix-identity-v2-3pid-bind) - Publish an association between a session and a Matrix user ID.
- [`POST /_matrix/identity/v2/3pid/unbind`](https://matrix.org/docs/spec/identity_service/r0.3.0#post-matrix-identity-v2-3pid-unbind) - Remove an association between a session and a Matrix user ID.
- [`POST /_matrix/identity/v2/store-invite`](https://matrix.org/docs/spec/identity_service/r0.3.0#post-matrix-identity-v2-store-invite) - Store pending invitations to a user's 3pid.
- [`POST /_matrix/identity/v2/sign-ed25519`](https://matrix.org/docs/spec/identity_service/r0.3.0#post-matrix-identity-v2-sign-ed25519) - Sign invitation details.
There is only one exception: [`POST /_matrix/identity/v2/terms`](https://matrix.org/docs/spec/identity_service/r0.3.0#post-matrix-identity-v2-terms) which uses for accepting the terms and requires the authorization.
## [Hash lookup](https://github.com/matrix-org/matrix-doc/blob/hs/hash-identity/proposals/2134-identity-hash-lookup.md)
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)
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)
```
When enabled and client requests the `none` algorithms then hash lookups works as v1 bulk lookup.
Delay specified in the format: `2d 4h 12m 34s` - this means 2 days 4 hours 12 minutes and 34 seconds. Zero units may be omitted. For example:
- 12s - 12 seconds
- 3m - 3 minutes
- 5m 6s - 5 minutes and 6 seconds
- 6h 3s - 6 hours and 3 seconds
Sha256 algorithm supports only sql, memory and exec 3PID providers.
For sql provider (i.e. for the `synapseSql`):
```.yaml
synapseSql:
lookup:
query: 'select user_id as mxid, medium, address from user_threepid_id_server' # query for retrive 3PIDs for hashes.
```
For general sql provider:
```.yaml
sql:
lookup:
query: 'select user as mxid, field1 as medium, field2 as address from some_table' # query for retrive 3PIDs for hashes.
```
Each query should return the `mxid`, `medium` and `address` fields.
For memory providers:
```.yaml
memory:
hashEnabled: true # enable the hash lookup (defaults is false)
```
For exec providers:
```.yaml
exec:
identity:
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 | | - Profile's 3PIDs |
| - 3PID Invites | | - 3PID Invites |

View File

@@ -1,14 +1,18 @@
# From source # From source
- [From source](#from-source)
- [Binaries](#binaries) - [Binaries](#binaries)
- [Requirements](#requirements) - [Requirements](#requirements)
- [Build](#build) - [Build](#build)
- [Debian package](#debian-package) - [Debian package](#debian-package)
- [Docker image](#docker-image) - [Docker image](#docker-image)
- [Multi-platform builds](#multi-platform-builds)
- [Next steps](#next-steps) - [Next steps](#next-steps)
## Binaries ## Binaries
### Requirements ### Requirements
- JDK 1.8 - JDK 1.8
- OpenJDK 11
- OpenJDK 14
### Build ### Build
```bash ```bash
@@ -17,7 +21,7 @@ cd ma1sd
./gradlew build ./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). For advanced configuration, see the [Configure section](configure.md).
Start the server in foreground to validate the build and configuration: 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. 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 ## Next steps
- [Integrate with your infrastructure](getting-started.md#integrate) - [Integrate with your infrastructure](getting-started.md#integrate)

View File

@@ -48,6 +48,9 @@ Create a list under the label `myOtherServers` containing two Identity servers:
## Unbind (MSC1915) ## Unbind (MSC1915)
- `session.policy.unbind.enabled`: Enable or disable unbind functionality (MSC1915). (Defaults to true). - `session.policy.unbind.enabled`: Enable or disable unbind functionality (MSC1915). (Defaults to true).
## Hash lookups, Term and others (MSC2140, MSC2134)
See the [dedicated document](MSC2140_MSC2134.md) for configuration.
*Warning*: Unbind check incoming request by two ways: *Warning*: Unbind check incoming request by two ways:
- session validation. - session validation.
- request signature via `X-Matrix` header and uses `server.publicUrl` property to construct the signing json; - request signature via `X-Matrix` header and uses `server.publicUrl` property to construct the signing json;
@@ -55,7 +58,53 @@ Commonly the `server.publicUrl` should be the same value as the `trusted_third_p
## Storage ## Storage
### SQLite ### 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 ## Identity stores
See the [Identity stores](stores/README.md) for specific configuration 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. 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? ### 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 The [synapse LDAP3 auth provider](https://github.com/matrix-org/matrix-synapse-ldap3) only handles one specific flow: validate credentials at login.
saying so and only handles on specific flow: validate credentials at login.
It does not: It does not:
- Auto-provision user profiles - Auto-provision user profiles

View File

@@ -1,5 +1,5 @@
# Identity # 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) - [Lookups](#lookups)
- [Invitations](#invitations) - [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. 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: In a typical configuration, you would end up with something similar to:
```yaml ```yaml
trusted_third_party_id_servers: trusted_third_party_id_servers:
- matrix.example.org - 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) ## Validate (Under reconstruction)
**NOTE:** In case your homeserver has no working federation, step 5 will not happen. If step 4 took place, consider **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 # Operations Guide
- [Operations Guide](#operations-guide)
- [Overview](#overview) - [Overview](#overview)
- [Maintenance](#maintenance) - [Maintenance](#maintenance)
- [Backuo](#backup) - [Backup](#backup)
- [Run](#run)
- [Restore](#restore)
## Overview ## Overview
This document gives various information for the day-to-day management and operations of ma1sd. This document gives various information for the day-to-day management and operations of ma1sd.

View File

@@ -89,7 +89,7 @@ ldap:
#### 3PIDs #### 3PIDs
You can also change the attribute lists for 3PID, like email or phone numbers. 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: for emails and phone number:
```yaml ```yaml
ldap: 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: 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. - `mxid` will use the ID as-is.
On each query, the first parameter `?` is set as a string with the corresponding ID format. On each query, the first parameter `?` is set as a string with the corresponding ID format.

View File

@@ -1,5 +1,5 @@
#Thu Jul 04 22:47:59 MSK 2019 #Thu Dec 05 22:39:36 MSK 2019
distributionUrl=https\://services.gradle.org/distributions/gradle-5.5.1-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

6
gradlew vendored
View File

@@ -7,7 +7,7 @@
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
# You may obtain a copy of the License at # You may obtain a copy of the License at
# #
# http://www.apache.org/licenses/LICENSE-2.0 # https://www.apache.org/licenses/LICENSE-2.0
# #
# Unless required by applicable law or agreed to in writing, software # Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, # distributed under the License is distributed on an "AS IS" BASIS,
@@ -125,8 +125,8 @@ if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi fi
# For Cygwin, switch paths to Windows format before running java # For Cygwin or MSYS, switch paths to Windows format before running java
if $cygwin ; then if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"` APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"` JAVACMD=`cygpath --unix "$JAVACMD"`

2
gradlew.bat vendored
View File

@@ -5,7 +5,7 @@
@rem you may not use this file except in compliance with 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 You may obtain a copy of the License at
@rem @rem
@rem http://www.apache.org/licenses/LICENSE-2.0 @rem https://www.apache.org/licenses/LICENSE-2.0
@rem @rem
@rem Unless required by applicable law or agreed to in writing, software @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 distributed under the License is distributed on an "AS IS" BASIS,

View File

@@ -1,111 +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: ''
################
# 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"

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> Maintainer: ma1uta <sablintolya@gmail.com>
Homepage: https://github.com/ma1uta/ma1sd Homepage: https://git.cqre.net/cqrenet/mxids.git
Description: Federated Matrix Identity Server Description: Federated Matrix Identity Server
Architecture: all Architecture: all
Section: net Section: net

View File

@@ -1,19 +1,19 @@
#!/bin/bash -e #!/bin/bash -e
# Add service account # Add service account
useradd -r ma1sd || true useradd -r mxids || true
# Set permissions for data directory # 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 # Create symlink to mxids run script
ln -sfT /usr/lib/ma1sd/ma1sd /usr/bin/ma1sd ln -sfT /usr/lib/mxids/mxids /usr/bin/mxids
# Enable systemd service # 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 # Specifically targeted at upgrades where the service needs to be restarted
if [ -f "%DEB_CONF_FILE%" ]; then if [ -f "%DEB_CONF_FILE%" ]; then
systemctl restart ma1sd.service systemctl restart mxids.service
fi fi

View File

@@ -1,10 +1,10 @@
#!/bin/bash #!/bin/bash
# Stop running instance if needed # Stop running instance if needed
systemctl stop ma1sd.service systemctl stop mxids.service
# Disable service if exists # Disable service if exists
systemctl disable ma1sd.service systemctl disable mxids.service
# remove symlink # 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" echo >> "$CONF_FILE_PATH"
fi fi
echo "Starting ma1sd..." echo "Starting mxids..."
echo echo
fi 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() { public static MatrixPath clientR0() {
return client().add("r0"); return client().add("v3");
} }
private StringBuilder path = new StringBuilder(); private StringBuilder path = new StringBuilder();

View File

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

View File

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

View File

@@ -1,47 +0,0 @@
/*
* matrix-java-sdk - Matrix Client SDK for Java
* 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.matrix.codec;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class MxSha256 {
private MessageDigest md;
public MxSha256() {
try {
md = MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}
public String hash(byte[] data) {
return MxBase64.encode(md.digest(data));
}
public String hash(String data) {
return hash(data.getBytes(StandardCharsets.UTF_8));
}
}

View File

@@ -20,9 +20,16 @@
package io.kamax.mxisd; package io.kamax.mxisd;
import io.kamax.mxisd.config.MatrixConfig;
import io.kamax.mxisd.config.MxisdConfig; 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.InternalInfoHandler;
import io.kamax.mxisd.http.undertow.handler.OptionsHandler; 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.SaneHandler;
import io.kamax.mxisd.http.undertow.handler.as.v1.AsNotFoundHandler; import io.kamax.mxisd.http.undertow.handler.as.v1.AsNotFoundHandler;
import io.kamax.mxisd.http.undertow.handler.as.v1.AsTransactionHandler; import io.kamax.mxisd.http.undertow.handler.as.v1.AsTransactionHandler;
@@ -31,19 +38,48 @@ import io.kamax.mxisd.http.undertow.handler.auth.RestAuthHandler;
import io.kamax.mxisd.http.undertow.handler.auth.v1.LoginGetHandler; import io.kamax.mxisd.http.undertow.handler.auth.v1.LoginGetHandler;
import io.kamax.mxisd.http.undertow.handler.auth.v1.LoginHandler; import io.kamax.mxisd.http.undertow.handler.auth.v1.LoginHandler;
import io.kamax.mxisd.http.undertow.handler.auth.v1.LoginPostHandler; import io.kamax.mxisd.http.undertow.handler.auth.v1.LoginPostHandler;
import io.kamax.mxisd.http.undertow.handler.auth.v2.AccountGetUserInfoHandler;
import io.kamax.mxisd.http.undertow.handler.auth.v2.AccountLogoutHandler;
import io.kamax.mxisd.http.undertow.handler.auth.v2.AccountRegisterHandler;
import io.kamax.mxisd.http.undertow.handler.directory.v1.UserDirectorySearchHandler; import io.kamax.mxisd.http.undertow.handler.directory.v1.UserDirectorySearchHandler;
import io.kamax.mxisd.http.undertow.handler.identity.v1.*; import io.kamax.mxisd.http.undertow.handler.identity.share.EphemeralKeyIsValidHandler;
import io.kamax.mxisd.http.undertow.handler.identity.share.HelloHandler;
import io.kamax.mxisd.http.undertow.handler.identity.share.KeyGetHandler;
import io.kamax.mxisd.http.undertow.handler.identity.share.RegularKeyIsValidHandler;
import io.kamax.mxisd.http.undertow.handler.identity.share.SessionStartHandler;
import io.kamax.mxisd.http.undertow.handler.identity.share.SessionTpidBindHandler;
import io.kamax.mxisd.http.undertow.handler.identity.share.SessionTpidGetValidatedHandler;
import io.kamax.mxisd.http.undertow.handler.identity.share.SessionTpidUnbindHandler;
import io.kamax.mxisd.http.undertow.handler.identity.share.SessionValidationGetHandler;
import io.kamax.mxisd.http.undertow.handler.identity.share.SessionValidationPostHandler;
import io.kamax.mxisd.http.undertow.handler.identity.share.SignEd25519Handler;
import io.kamax.mxisd.http.undertow.handler.identity.share.StoreInviteHandler;
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.invite.v1.RoomInviteHandler;
import io.kamax.mxisd.http.undertow.handler.profile.v1.InternalProfileHandler; import io.kamax.mxisd.http.undertow.handler.profile.v1.InternalProfileHandler;
import io.kamax.mxisd.http.undertow.handler.profile.v1.ProfileHandler; import io.kamax.mxisd.http.undertow.handler.profile.v1.ProfileHandler;
import io.kamax.mxisd.http.undertow.handler.register.v1.Register3pidRequestTokenHandler; import io.kamax.mxisd.http.undertow.handler.register.v1.Register3pidRequestTokenHandler;
import io.kamax.mxisd.http.undertow.handler.status.StatusHandler; import io.kamax.mxisd.http.undertow.handler.status.StatusHandler;
import io.kamax.mxisd.http.undertow.handler.status.VersionHandler; import io.kamax.mxisd.http.undertow.handler.status.VersionHandler;
import io.kamax.mxisd.http.undertow.handler.term.v2.AcceptTermsHandler;
import io.kamax.mxisd.http.undertow.handler.term.v2.GetTermsHandler;
import io.kamax.mxisd.matrix.IdentityServiceAPI;
import io.undertow.Handlers; import io.undertow.Handlers;
import io.undertow.Undertow; import io.undertow.Undertow;
import io.undertow.server.HttpHandler; import io.undertow.server.HttpHandler;
import io.undertow.server.RoutingHandler;
import io.undertow.util.HttpString;
import io.undertow.util.Methods;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.regex.Pattern;
public class HttpMxisd { public class HttpMxisd {
@@ -66,58 +102,35 @@ public class HttpMxisd {
public void start() { public void start() {
m.start(); m.start();
HttpHandler helloHandler = SaneHandler.around(new HelloHandler()); HttpHandler asUserHandler = sane(new AsUserHandler(m.getAs()));
HttpHandler asTxnHandler = sane(new AsTransactionHandler(m.getAs()));
HttpHandler asNotFoundHandler = sane(new AsNotFoundHandler(m.getAs()));
HttpHandler asUserHandler = SaneHandler.around(new AsUserHandler(m.getAs())); final RoutingHandler handler = Handlers.routing()
HttpHandler asTxnHandler = SaneHandler.around(new AsTransactionHandler(m.getAs())); .add("OPTIONS", "/**", sane(new OptionsHandler()))
HttpHandler asNotFoundHandler = SaneHandler.around(new AsNotFoundHandler(m.getAs()));
HttpHandler storeInvHandler = SaneHandler.around(new StoreInviteHandler(m.getConfig().getServer(), m.getInvite(), m.getKeyManager()));
httpSrv = Undertow.builder().addHttpListener(m.getConfig().getServer().getPort(), "0.0.0.0").setHandler(Handlers.routing()
.add("OPTIONS", "/**", SaneHandler.around(new OptionsHandler()))
// Status endpoints // Status endpoints
.get(StatusHandler.Path, SaneHandler.around(new StatusHandler())) .get(StatusHandler.Path, sane(new StatusHandler()))
.get(VersionHandler.Path, SaneHandler.around(new VersionHandler())) .get(VersionHandler.Path, sane(new VersionHandler()))
// Authentication endpoints // Authentication endpoints
.get(LoginHandler.Path, SaneHandler.around(new LoginGetHandler(m.getAuth(), m.getHttpClient()))) .get(LoginHandler.Path, sane(new LoginGetHandler(m.getAuth(), m.getHttpClient())))
.post(LoginHandler.Path, SaneHandler.around(new LoginPostHandler(m.getAuth()))) .post(LoginHandler.Path, sane(new LoginPostHandler(m.getAuth())))
.post(RestAuthHandler.Path, SaneHandler.around(new RestAuthHandler(m.getAuth()))) .post(RestAuthHandler.Path, sane(new RestAuthHandler(m.getAuth())))
// Directory endpoints // Directory endpoints
.post(UserDirectorySearchHandler.Path, SaneHandler.around(new UserDirectorySearchHandler(m.getDirectory()))) .post(UserDirectorySearchHandler.Path, sane(new UserDirectorySearchHandler(m.getDirectory())))
// Key endpoints
.get(KeyGetHandler.Path, SaneHandler.around(new KeyGetHandler(m.getKeyManager())))
.get(RegularKeyIsValidHandler.Path, SaneHandler.around(new RegularKeyIsValidHandler(m.getKeyManager())))
.get(EphemeralKeyIsValidHandler.Path, SaneHandler.around(new EphemeralKeyIsValidHandler(m.getKeyManager())))
// Identity endpoints
.get(HelloHandler.Path, helloHandler)
.get(HelloHandler.Path + "/", helloHandler) // Be lax with possibly trailing slash
.get(SingleLookupHandler.Path, SaneHandler.around(new SingleLookupHandler(m.getConfig(), m.getIdentity(), m.getSign())))
.post(BulkLookupHandler.Path, SaneHandler.around(new BulkLookupHandler(m.getIdentity())))
.post(StoreInviteHandler.Path, storeInvHandler)
.post(SessionStartHandler.Path, SaneHandler.around(new SessionStartHandler(m.getSession())))
.get(SessionValidateHandler.Path, SaneHandler.around(new SessionValidationGetHandler(m.getSession(), m.getConfig())))
.post(SessionValidateHandler.Path, SaneHandler.around(new SessionValidationPostHandler(m.getSession())))
.get(SessionTpidGetValidatedHandler.Path, SaneHandler.around(new SessionTpidGetValidatedHandler(m.getSession())))
.post(SessionTpidBindHandler.Path, SaneHandler.around(new SessionTpidBindHandler(m.getSession(), m.getInvite(), m.getSign())))
.post(SessionTpidUnbindHandler.Path, SaneHandler.around(new SessionTpidUnbindHandler(m.getSession())))
.post(SignEd25519Handler.Path, SaneHandler.around(new SignEd25519Handler(m.getConfig(), m.getInvite(), m.getSign())))
// Profile endpoints // Profile endpoints
.get(ProfileHandler.Path, SaneHandler.around(new ProfileHandler(m.getProfile()))) .get(ProfileHandler.Path, sane(new ProfileHandler(m.getProfile())))
.get(InternalProfileHandler.Path, SaneHandler.around(new InternalProfileHandler(m.getProfile()))) .get(InternalProfileHandler.Path, sane(new InternalProfileHandler(m.getProfile())))
// Registration endpoints // Registration endpoints
.post(Register3pidRequestTokenHandler.Path, SaneHandler.around(new Register3pidRequestTokenHandler(m.getReg(), m.getClientDns(), m.getHttpClient()))) .post(Register3pidRequestTokenHandler.Path,
sane(new Register3pidRequestTokenHandler(m.getReg(), m.getClientDns(), m.getHttpClient())))
// Invite endpoints // 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 // Application Service endpoints
.get(AsUserHandler.Path, asUserHandler) .get(AsUserHandler.Path, asUserHandler)
@@ -129,18 +142,143 @@ public class HttpMxisd {
.put("/transactions/{" + AsTransactionHandler.ID + "}", asTxnHandler) // Legacy endpoint .put("/transactions/{" + AsTransactionHandler.ID + "}", asTxnHandler) // Legacy endpoint
// Banned endpoints // 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);
).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(); httpSrv.start();
} }
public void stop() { public void stop() {
// Because it might have never been initialized if an exception is thrown early // Because it might have never been initialized if an exception is thrown early
if (Objects.nonNull(httpSrv)) httpSrv.stop(); if (Objects.nonNull(httpSrv)) {
httpSrv.stop();
}
m.stop(); m.stop();
} }
private void keyEndpoints(RoutingHandler routingHandler) {
addEndpoints(routingHandler, Methods.GET, false,
new KeyGetHandler(m.getKeyManager()),
new RegularKeyIsValidHandler(m.getKeyManager()),
new EphemeralKeyIsValidHandler(m.getKeyManager())
);
}
private void identityEndpoints(RoutingHandler routingHandler) {
// Legacy v1
routingHandler.get(SingleLookupHandler.Path, sane(new SingleLookupHandler(m.getConfig(), m.getIdentity(), m.getSign())));
routingHandler.post(BulkLookupHandler.Path, sane(new BulkLookupHandler(m.getIdentity())));
addEndpoints(routingHandler, Methods.GET, false, new HelloHandler());
addEndpoints(routingHandler, Methods.GET, true,
new SessionValidationGetHandler(m.getSession(), m.getConfig()),
new SessionTpidGetValidatedHandler(m.getSession())
);
addEndpoints(routingHandler, Methods.POST, true,
new StoreInviteHandler(m.getConfig().getServer(), m.getInvite(), m.getKeyManager()),
new SessionStartHandler(m.getSession()),
new SessionValidationPostHandler(m.getSession()),
new SessionTpidBindHandler(m.getSession(), m.getInvite(), m.getSign()),
new SessionTpidUnbindHandler(m.getSession()),
new SignEd25519Handler(m.getConfig(), m.getInvite(), m.getSign())
);
}
private void accountEndpoints(RoutingHandler routingHandler) {
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) {
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) {
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) {
for (ApiHandler handler : handlers) {
attachHandler(routingHandler, method, handler, useAuthorization, sane(handler));
}
}
private void attachHandler(RoutingHandler routingHandler, HttpString method, ApiHandler apiHandler, boolean useAuthorization,
HttpHandler httpHandler) {
MatrixConfig matrixConfig = m.getConfig().getMatrix();
if (matrixConfig.isV1()) {
routingHandler.add(method, apiHandler.getPath(IdentityServiceAPI.V1), sane(httpHandler));
}
if (matrixConfig.isV2()) {
wrapWithTokenAndAuthorizationHandlers(routingHandler, method, httpHandler, apiHandler.getPath(IdentityServiceAPI.V2),
useAuthorization);
}
}
private void wrapWithTokenAndAuthorizationHandlers(RoutingHandler routingHandler, HttpString method, HttpHandler httpHandler,
String url, boolean useAuthorization) {
List<PolicyConfig.PolicyObject> policyObjects = getPolicyObjects(url);
HttpHandler wrappedHandler;
if (useAuthorization) {
wrappedHandler = policyObjects.isEmpty() ? httpHandler : CheckTermsHandler.around(m.getAccMgr(), httpHandler, policyObjects);
wrappedHandler = AuthorizationHandler.around(m.getAccMgr(), wrappedHandler);
} else {
wrappedHandler = httpHandler;
}
routingHandler.add(method, url, sane(wrappedHandler));
}
@NotNull
private List<PolicyConfig.PolicyObject> getPolicyObjects(String url) {
PolicyConfig policyConfig = m.getConfig().getPolicy();
List<PolicyConfig.PolicyObject> policies = new ArrayList<>();
if (!policyConfig.getPolicies().isEmpty()) {
for (PolicyConfig.PolicyObject policy : policyConfig.getPolicies().values()) {
for (Pattern pattern : policy.getPatterns()) {
if (pattern.matcher(url).matches()) {
policies.add(policy);
}
}
}
}
return policies;
}
private HttpHandler sane(HttpHandler httpHandler) {
SaneHandler handler = SaneHandler.around(httpHandler);
if (m.getConfig().getLogging().isRequests()) {
return new RequestDumpingHandler(handler);
} else {
return handler;
}
}
} }

View File

@@ -21,11 +21,13 @@
package io.kamax.mxisd; package io.kamax.mxisd;
import io.kamax.mxisd.as.AppSvcManager; import io.kamax.mxisd.as.AppSvcManager;
import io.kamax.mxisd.auth.AccountManager;
import io.kamax.mxisd.auth.AuthManager; import io.kamax.mxisd.auth.AuthManager;
import io.kamax.mxisd.auth.AuthProviders; import io.kamax.mxisd.auth.AuthProviders;
import io.kamax.mxisd.backend.IdentityStoreSupplier; import io.kamax.mxisd.backend.IdentityStoreSupplier;
import io.kamax.mxisd.backend.sql.synapse.Synapse; import io.kamax.mxisd.backend.sql.synapse.Synapse;
import io.kamax.mxisd.config.MxisdConfig; import io.kamax.mxisd.config.MxisdConfig;
import io.kamax.mxisd.config.StorageConfig;
import io.kamax.mxisd.crypto.CryptoFactory; import io.kamax.mxisd.crypto.CryptoFactory;
import io.kamax.mxisd.crypto.KeyManager; import io.kamax.mxisd.crypto.KeyManager;
import io.kamax.mxisd.crypto.SignatureManager; import io.kamax.mxisd.crypto.SignatureManager;
@@ -34,6 +36,7 @@ import io.kamax.mxisd.directory.DirectoryManager;
import io.kamax.mxisd.directory.DirectoryProviders; import io.kamax.mxisd.directory.DirectoryProviders;
import io.kamax.mxisd.dns.ClientDnsOverwrite; import io.kamax.mxisd.dns.ClientDnsOverwrite;
import io.kamax.mxisd.dns.FederationDnsOverwrite; import io.kamax.mxisd.dns.FederationDnsOverwrite;
import io.kamax.mxisd.hash.HashManager;
import io.kamax.mxisd.invitation.InvitationManager; import io.kamax.mxisd.invitation.InvitationManager;
import io.kamax.mxisd.lookup.ThreePidProviders; import io.kamax.mxisd.lookup.ThreePidProviders;
import io.kamax.mxisd.lookup.fetcher.IRemoteIdentityServerFetcher; import io.kamax.mxisd.lookup.fetcher.IRemoteIdentityServerFetcher;
@@ -52,7 +55,7 @@ import io.kamax.mxisd.registration.RegistrationManager;
import io.kamax.mxisd.session.SessionManager; import io.kamax.mxisd.session.SessionManager;
import io.kamax.mxisd.storage.IStorage; import io.kamax.mxisd.storage.IStorage;
import io.kamax.mxisd.storage.ormlite.OrmLiteSqlStorage; 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.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.client.HttpClients;
@@ -64,7 +67,7 @@ public class Mxisd {
public static final String Version = StringUtils.defaultIfBlank(Mxisd.class.getPackage().getImplementationVersion(), "UNKNOWN"); public static final String Version = StringUtils.defaultIfBlank(Mxisd.class.getPackage().getImplementationVersion(), "UNKNOWN");
public static final String Agent = Name + "/" + Version; public static final String Agent = Name + "/" + Version;
private MxisdConfig cfg; private final MxisdConfig cfg;
private CloseableHttpClient httpClient; private CloseableHttpClient httpClient;
private IRemoteIdentityServerFetcher srvFetcher; private IRemoteIdentityServerFetcher srvFetcher;
@@ -85,6 +88,8 @@ public class Mxisd {
private SessionManager sessMgr; private SessionManager sessMgr;
private NotificationManager notifMgr; private NotificationManager notifMgr;
private RegistrationManager regMgr; private RegistrationManager regMgr;
private AccountManager accMgr;
private HashManager hashManager;
// HS-specific classes // HS-specific classes
private Synapse synapse; private Synapse synapse;
@@ -105,7 +110,10 @@ public class Mxisd {
IdentityServerUtils.setHttpClient(httpClient); IdentityServerUtils.setHttpClient(httpClient);
srvFetcher = new RemoteIdentityServerFetcher(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()); keyMgr = CryptoFactory.getKeyManager(cfg.getKey());
signMgr = CryptoFactory.getSignatureManager(cfg, keyMgr); signMgr = CryptoFactory.getSignatureManager(cfg, keyMgr);
clientDns = new ClientDnsOverwrite(cfg.getDns().getOverwrite()); clientDns = new ClientDnsOverwrite(cfg.getDns().getOverwrite());
@@ -115,15 +123,19 @@ public class Mxisd {
ServiceLoader.load(IdentityStoreSupplier.class).iterator().forEachRemaining(p -> p.accept(this)); ServiceLoader.load(IdentityStoreSupplier.class).iterator().forEachRemaining(p -> p.accept(this));
ServiceLoader.load(NotificationHandlerSupplier.class).iterator().forEachRemaining(p -> p.accept(this)); ServiceLoader.load(NotificationHandlerSupplier.class).iterator().forEachRemaining(p -> p.accept(this));
idStrategy = new RecursivePriorityLookupStrategy(cfg.getLookup(), ThreePidProviders.get(), bridgeFetcher); hashManager = new HashManager();
hashManager.init(cfg.getHashing(), ThreePidProviders.get(), store);
idStrategy = new RecursivePriorityLookupStrategy(cfg.getLookup(), ThreePidProviders.get(), bridgeFetcher, hashManager);
pMgr = new ProfileManager(ProfileProviders.get(), clientDns, httpClient); pMgr = new ProfileManager(ProfileProviders.get(), clientDns, httpClient);
notifMgr = new NotificationManager(cfg.getNotification(), NotificationHandlers.get()); notifMgr = new NotificationManager(cfg.getNotification(), NotificationHandlers.get());
sessMgr = new SessionManager(cfg, store, notifMgr, resolver, httpClient, signMgr); sessMgr = new SessionManager(cfg, store, notifMgr, resolver, signMgr);
invMgr = new InvitationManager(cfg, store, idStrategy, keyMgr, signMgr, resolver, notifMgr, pMgr); invMgr = new InvitationManager(cfg, store, idStrategy, keyMgr, signMgr, resolver, notifMgr, pMgr);
authMgr = new AuthManager(cfg, AuthProviders.get(), idStrategy, invMgr, clientDns, httpClient); authMgr = new AuthManager(cfg, AuthProviders.get(), idStrategy, invMgr, clientDns, httpClient);
dirMgr = new DirectoryManager(cfg.getDirectory(), clientDns, httpClient, DirectoryProviders.get()); dirMgr = new DirectoryManager(cfg.getDirectory(), clientDns, httpClient, DirectoryProviders.get());
regMgr = new RegistrationManager(cfg.getRegister(), httpClient, clientDns, invMgr); regMgr = new RegistrationManager(cfg.getRegister(), httpClient, clientDns, invMgr);
asHander = new AppSvcManager(this); asHander = new AppSvcManager(this);
accMgr = new AccountManager(store, resolver, cfg.getAccountConfig(), cfg.getMatrix());
} }
public MxisdConfig getConfig() { public MxisdConfig getConfig() {
@@ -194,6 +206,14 @@ public class Mxisd {
return synapse; return synapse;
} }
public AccountManager getAccMgr() {
return accMgr;
}
public HashManager getHashManager() {
return hashManager;
}
public void start() { public void start() {
build(); build();
} }

View File

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

View File

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

View File

@@ -48,6 +48,7 @@ import org.slf4j.LoggerFactory;
import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.introspector.BeanAccess; import org.yaml.snakeyaml.introspector.BeanAccess;
import org.yaml.snakeyaml.representer.Representer; import org.yaml.snakeyaml.representer.Representer;
import org.yaml.snakeyaml.DumperOptions;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
@@ -148,12 +149,12 @@ public class AppSvcManager {
SynapseRegistrationYaml syncCfg = SynapseRegistrationYaml.parse(cfg.getAppsvc(), cfg.getMatrix().getDomain()); 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); rep.getPropertyUtils().setBeanAccess(BeanAccess.FIELD);
Yaml yaml = new Yaml(rep); 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+"))); List<String> lines = new ArrayList<>(Arrays.asList(yaml.dump(syncCfg).split("\\R+")));
if (StringUtils.equals(lines.get(0), "!!" + SynapseRegistrationYaml.class.getCanonicalName())) { if (StringUtils.equals(lines.get(0), "!!" + SynapseRegistrationYaml.class.getCanonicalName())) {
lines.remove(0); lines.remove(0);

View File

@@ -25,8 +25,8 @@ import io.kamax.matrix.hs._MatrixRoom;
import io.kamax.mxisd.Mxisd; import io.kamax.mxisd.Mxisd;
import io.kamax.mxisd.invitation.IThreePidInviteReply; import io.kamax.mxisd.invitation.IThreePidInviteReply;
import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.CommandLine;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang.text.StrBuilder; import org.apache.commons.lang3.text.StrBuilder;
import java.util.List; 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.Mxisd;
import io.kamax.mxisd.lookup.SingleLookupReply; import io.kamax.mxisd.lookup.SingleLookupReply;
import org.apache.commons.cli.CommandLine; 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 org.apache.commons.lang3.StringUtils;
import java.util.Optional; 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.invitation.MatrixIdInvite;
import io.kamax.mxisd.notification.NotificationManager; import io.kamax.mxisd.notification.NotificationManager;
import io.kamax.mxisd.profile.ProfileManager; 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.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -144,7 +144,13 @@ public class MembershipEventProcessor implements EventTypeProcessor {
.collect(Collectors.toList()); .collect(Collectors.toList());
log.info("Found {} email(s) in identity store for {}", tpids.size(), inviteeId); 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()); log.info("Found Email to notify about room invitation: {}", tpid.getAddress());
Map<String, String> properties = new HashMap<>(); Map<String, String> properties = new HashMap<>();
profiler.getDisplayName(sender).ifPresent(name -> properties.put("sender_display_name", name)); 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.LookupCommandProcessor;
import io.kamax.mxisd.as.processor.command.PingCommandProcessor; import io.kamax.mxisd.as.processor.command.PingCommandProcessor;
import org.apache.commons.cli.*; 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.apache.commons.lang3.StringUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;

View File

@@ -0,0 +1,166 @@
package io.kamax.mxisd.auth;
import com.google.gson.JsonObject;
import io.kamax.matrix.MatrixID;
import io.kamax.matrix.json.GsonUtil;
import io.kamax.mxisd.config.AccountConfig;
import io.kamax.mxisd.config.MatrixConfig;
import io.kamax.mxisd.config.PolicyConfig;
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;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.time.Instant;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
public class AccountManager {
private static final Logger LOGGER = LoggerFactory.getLogger(AccountManager.class);
private final IStorage storage;
private final HomeserverFederationResolver resolver;
private final AccountConfig accountConfig;
private final MatrixConfig matrixConfig;
public AccountManager(IStorage storage, HomeserverFederationResolver resolver, AccountConfig accountConfig, MatrixConfig matrixConfig) {
this.storage = storage;
this.resolver = resolver;
this.accountConfig = accountConfig;
this.matrixConfig = matrixConfig;
}
public String register(OpenIdToken openIdToken) {
Objects.requireNonNull(openIdToken.getAccessToken(), "Missing required access_token");
Objects.requireNonNull(openIdToken.getTokenType(), "Missing required token type");
Objects.requireNonNull(openIdToken.getMatrixServerName(), "Missing required matrix domain");
LOGGER.info("Registration from the server: {}", openIdToken.getMatrixServerName());
String userId = getUserId(openIdToken);
LOGGER.info("UserId: {}", userId);
String token = UUID.randomUUID().toString();
AccountDao account = new AccountDao(openIdToken.getAccessToken(), openIdToken.getTokenType(),
openIdToken.getMatrixServerName(), openIdToken.getExpiresIn(),
Instant.now().getEpochSecond(), userId, token);
storage.insertToken(account);
LOGGER.info("User {} registered", userId);
return token;
}
private String getUserId(OpenIdToken openIdToken) {
String matrixServerName = openIdToken.getMatrixServerName();
HomeserverFederationResolver.HomeserverTarget homeserverTarget = resolver.resolve(matrixServerName);
String homeserverURL = homeserverTarget.getUrl().toString();
LOGGER.info("Domain resolved: {} => {}", matrixServerName, homeserverURL);
HttpGet getUserInfo = new HttpGet(
homeserverURL + "/_matrix/federation/v1/openid/userinfo?access_token=" + openIdToken.getAccessToken());
String userId;
try (CloseableHttpClient httpClient = HttpClients.custom()
.setSSLHostnameVerifier(new HomeserverVerifier(homeserverTarget.getDomain())).build()) {
try (CloseableHttpResponse response = httpClient.execute(getUserInfo)) {
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == HttpStatus.SC_OK) {
String content = EntityUtils.toString(response.getEntity());
LOGGER.trace("Response: {}", content);
JsonObject body = GsonUtil.parseObj(content);
userId = GsonUtil.getStringOrThrow(body, "sub");
} else {
LOGGER.error("Wrong response status: {}", statusCode);
throw new InvalidCredentialsException();
}
} catch (IOException e) {
LOGGER.error("Unable to get user info.", e);
throw new InvalidCredentialsException();
}
} catch (IOException e) {
LOGGER.error("Unable to create a connection to host: " + homeserverURL, e);
throw new InvalidCredentialsException();
}
checkMXID(userId);
return userId;
}
private void checkMXID(String userId) {
MatrixID mxid;
try {
mxid = MatrixID.asValid(userId);
} catch (IllegalArgumentException e) {
LOGGER.error("Wrong MXID: " + userId, e);
throw new BadRequestException("Wrong MXID");
}
if (getAccountConfig().isAllowOnlyTrustDomains()) {
LOGGER.info("Allow registration only for trust domain.");
if (getMatrixConfig().getDomain().equals(mxid.getDomain())) {
LOGGER.info("Allow user {} to registration", userId);
} else {
LOGGER.error("Deny user {} to registration", userId);
throw new InvalidCredentialsException();
}
} else {
LOGGER.info("Allow registration from any server.");
}
}
public String getUserId(String token) {
return storage.findAccount(token).orElseThrow(NotFoundException::new).getUserId();
}
public AccountDao findAccount(String token) {
AccountDao accountDao = storage.findAccount(token).orElse(null);
if (LOGGER.isInfoEnabled()) {
if (accountDao != null) {
LOGGER.info("Found account for user: {}", accountDao.getUserId());
} else {
LOGGER.warn("Account not found.");
}
}
return accountDao;
}
public void logout(String token) {
String userId = storage.findAccount(token).orElseThrow(InvalidCredentialsException::new).getUserId();
LOGGER.info("Logout: {}", userId);
deleteAccount(token);
}
public void deleteAccount(String token) {
storage.deleteAccepts(token);
storage.deleteToken(token);
}
public void acceptTerm(String token, String url) {
storage.acceptTerm(token, url);
}
public boolean isTermAccepted(String token, List<PolicyConfig.PolicyObject> policies) {
return policies.isEmpty() || storage.isTermAccepted(token, policies);
}
public AccountConfig getAccountConfig() {
return accountConfig;
}
public MatrixConfig getMatrixConfig() {
return matrixConfig;
}
}

View File

@@ -45,7 +45,7 @@ import io.kamax.mxisd.lookup.ThreePidMapping;
import io.kamax.mxisd.lookup.strategy.LookupStrategy; import io.kamax.mxisd.lookup.strategy.LookupStrategy;
import io.kamax.mxisd.util.RestClientUtils; import io.kamax.mxisd.util.RestClientUtils;
import org.apache.commons.io.IOUtils; 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.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpPost;

View File

@@ -0,0 +1,50 @@
package io.kamax.mxisd.auth;
import com.google.gson.annotations.SerializedName;
public class OpenIdToken {
@SerializedName("access_token")
private String accessToken;
@SerializedName("token_type")
private String tokenType;
@SerializedName("matrix_server_name")
private String matrixServerName;
@SerializedName("expires_in")
private long expiresIn;
public String getAccessToken() {
return accessToken;
}
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
public String getTokenType() {
return tokenType;
}
public void setTokenType(String tokenType) {
this.tokenType = tokenType;
}
public String getMatrixServerName() {
return matrixServerName;
}
public void setMatrixServerName(String matrixServerName) {
this.matrixServerName = matrixServerName;
}
public long getExpiresIn() {
return expiresIn;
}
public void setExpiresIn(long expiresIn) {
this.expiresIn = expiresIn;
}
}

View File

@@ -164,6 +164,30 @@ public class ExecIdentityStore extends ExecStore implements IThreePidProvider {
return input.toString(); return input.toString();
}); });
addBulkSuccessMapper(p);
p.withFailureDefault(output -> Collections.emptyList());
return p.execute();
}
@Override
public Iterable<ThreePidMapping> populateHashes() {
if (!cfg.isHashLookup()) {
return Collections.emptyList();
}
Processor<List<ThreePidMapping>> p = new Processor<>();
p.withConfig(cfg.getLookup().getBulk());
addBulkSuccessMapper(p);
p.withFailureDefault(output -> Collections.emptyList());
return p.execute();
}
private void addBulkSuccessMapper(Processor<List<ThreePidMapping>> p) {
p.addSuccessMapper(JsonType, output -> { p.addSuccessMapper(JsonType, output -> {
if (StringUtils.isBlank(output)) { if (StringUtils.isBlank(output)) {
return Collections.emptyList(); return Collections.emptyList();
@@ -188,10 +212,5 @@ public class ExecIdentityStore extends ExecStore implements IThreePidProvider {
throw new InternalServerError("Invalid user type: " + item.getId().getType()); throw new InternalServerError("Invalid user type: " + item.getId().getType());
}).collect(Collectors.toList()); }).collect(Collectors.toList());
}); });
p.withFailureDefault(output -> Collections.emptyList());
return p.execute();
} }
} }

View File

@@ -27,7 +27,7 @@ import io.kamax.mxisd.config.ExecConfig;
import io.kamax.mxisd.profile.JsonProfileRequest; import io.kamax.mxisd.profile.JsonProfileRequest;
import io.kamax.mxisd.profile.JsonProfileResult; import io.kamax.mxisd.profile.JsonProfileResult;
import io.kamax.mxisd.profile.ProfileProvider; 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.Collections;
import java.util.List; 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; package io.kamax.mxisd.backend.firebase;
import com.google.firebase.auth.UserInfo; import com.google.firebase.auth.FirebaseAuth;
import com.google.i18n.phonenumbers.NumberParseException; import com.google.firebase.auth.FirebaseAuthException;
import com.google.i18n.phonenumbers.PhoneNumberUtil; import com.google.firebase.auth.FirebaseToken;
import io.kamax.matrix.ThreePid; import com.google.firebase.auth.UserRecord;
import io.kamax.matrix.ThreePidMedium;
import io.kamax.matrix._MatrixID; import io.kamax.matrix._MatrixID;
import io.kamax.mxisd.UserIdType;
import io.kamax.mxisd.auth.provider.AuthenticatorProvider; import io.kamax.mxisd.auth.provider.AuthenticatorProvider;
import io.kamax.mxisd.auth.provider.BackendAuthResult; import io.kamax.mxisd.auth.provider.BackendAuthResult;
import io.kamax.mxisd.config.FirebaseConfig; import io.kamax.mxisd.config.FirebaseConfig;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit; 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) { public GoogleFirebaseAuthenticator(FirebaseConfig config) {
this(cfg.isEnabled(), cfg.getCredentials(), cfg.getDatabase()); this.config = config;
} }
public GoogleFirebaseAuthenticator(boolean isEnabled, String credsPath, String db) { @Override
super(isEnabled, "AuthenticationProvider", credsPath, db); public boolean isEnabled() {
} return this.config.isEnabled();
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 @Override
public BackendAuthResult authenticate(_MatrixID mxid, String password) { public BackendAuthResult authenticate(_MatrixID mxid, String password) {
if (!isEnabled()) { if (!isEnabled()) {
throw new IllegalStateException(); log.warn("Firebase authenticator is disabled.");
return BackendAuthResult.failure();
} }
log.info("Trying to authenticate {}", mxid); CompletableFuture<BackendAuthResult> resultFuture = new CompletableFuture<>();
executor.submit(() -> {
final BackendAuthResult result = BackendAuthResult.failure();
String localpart = mxid.getLocalPart();
CountDownLatch l = new CountDownLatch(1);
getFirebase().verifyIdToken(password).addOnSuccessListener(token -> {
try { try {
if (!StringUtils.equals(localpart, token.getUid())) { FirebaseToken decodedToken = FirebaseAuth.getInstance().verifyIdToken(password);
log.info("Failure to authenticate {}: Matrix ID localpart '{}' does not match Firebase UID '{}'", mxid, localpart, token.getUid()); if (!mxid.getLocalPart().equals(decodedToken.getUid())) {
result.fail(); log.warn("UID mismatch for user {}", mxid);
resultFuture.complete(BackendAuthResult.failure());
return; return;
} }
result.succeed(mxid.getId(), UserIdType.MatrixID.getId(), token.getName()); // Assuming you have a method to convert Firebase user info into BackendAuthResult
log.info("{} was successfully authenticated", mxid); resultFuture.complete(convertToAuthResult(decodedToken));
log.info("Fetching profile for {}", mxid); } catch (FirebaseAuthException e) {
CountDownLatch userRecordLatch = new CountDownLatch(1); log.error("Failed to authenticate user {}: {}", mxid, e.getMessage(), e);
getFirebase().getUser(token.getUid()).addOnSuccessListener(user -> { resultFuture.complete(BackendAuthResult.failure());
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 { try {
if (e instanceof IllegalArgumentException) { return resultFuture.get(); // This will block, consider using thenAccept or similar for a truly non-blocking approach
log.info("Failure to authenticate {}: invalid firebase token", mxid); } catch (Exception e) {
} else { log.error("Error during authentication process", e);
log.info("Failure to authenticate {}: {}", mxid, e.getMessage(), e); return BackendAuthResult.failure();
log.info("Exception", e); }
} }
result.fail(); private BackendAuthResult convertToAuthResult(FirebaseToken decodedToken) {
} finally { String userId = decodedToken.getUid(); // UID from Firebase as the user ID
l.countDown(); String userIdType = "MatrixID"; // Assuming you're using string literals for user ID types
} String displayName = decodedToken.getName(); // Display name from the Firebase token
});
waitOnLatch(result, l, "Firebase auth check"); // Adjust the method call according to the actual parameters it expects.
return result; // 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; package io.kamax.mxisd.backend.firebase;
import com.google.auth.oauth2.GoogleCredentials;
import com.google.firebase.FirebaseApp; import com.google.firebase.FirebaseApp;
import com.google.firebase.FirebaseOptions; 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.FileInputStream;
import java.io.IOException; import java.io.IOException;
public class GoogleFirebaseBackend { public abstract class GoogleFirebaseBackend {
protected boolean enabled;
private transient final Logger log = LoggerFactory.getLogger(GoogleFirebaseBackend.class); protected String backendName;
protected String credentialsPath;
private boolean isEnabled; protected String databaseUrl;
private FirebaseAuth fbAuth;
protected FirebaseDatabase fbDb;
GoogleFirebaseBackend(boolean isEnabled, String name, String credsPath, String db) {
this.isEnabled = isEnabled;
if (!isEnabled) {
return;
}
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 { try {
FirebaseApp fbApp = FirebaseApp.initializeApp(getOpts(credsPath, db), name); initializeFirebase();
fbAuth = FirebaseAuth.getInstance(fbApp);
FirebaseDatabase.getInstance(fbApp);
log.info("Google Firebase Authentication is ready");
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException("Error when initializing Firebase", e); throw new RuntimeException("Failed to initialize Firebase", e);
}
} }
} }
private FirebaseCredential getCreds(String credsPath) throws IOException { private void initializeFirebase() throws IOException {
if (StringUtils.isNotBlank(credsPath)) { FileInputStream serviceAccount = new FileInputStream(credentialsPath);
try (FileInputStream is = new FileInputStream(credsPath)) {
return FirebaseCredentials.fromCertificate(is);
}
} else {
return FirebaseCredentials.applicationDefault();
}
}
private FirebaseOptions getOpts(String credsPath, String db) throws IOException { FirebaseOptions options = new FirebaseOptions.Builder()
if (StringUtils.isBlank(db)) { .setCredentials(GoogleCredentials.fromStream(serviceAccount))
throw new IllegalArgumentException("Firebase database is not configured"); .setDatabaseUrl(databaseUrl)
}
return new FirebaseOptions.Builder()
.setCredential(getCreds(credsPath))
.setDatabaseUrl(db)
.build(); .build();
if (FirebaseApp.getApps().isEmpty()) { // Check if Firebase has been initialized already
FirebaseApp.initializeApp(options);
}
} }
FirebaseAuth getFirebase() { // Additional methods for GoogleFirebaseBackend
return fbAuth;
}
public boolean isEnabled() {
return isEnabled;
}
} }

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; 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.MatrixID;
import io.kamax.matrix.ThreePidMedium; import io.kamax.matrix.ThreePidMedium;
import io.kamax.mxisd.config.MxisdConfig; import io.kamax.mxisd.config.MxisdConfig;
@@ -36,25 +13,22 @@ import org.slf4j.LoggerFactory;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
public class GoogleFirebaseProvider extends GoogleFirebaseBackend implements IThreePidProvider { 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; private String domain;
public GoogleFirebaseProvider(MxisdConfig cfg) { 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) { private String getMxid(String uid) {
super(isEnabled, "ThreePidProvider", credsPath, db); // Mock UID to MXID conversion
this.domain = domain; return MatrixID.asAcceptable(uid, domain).getId();
}
private String getMxid(UserRecord record) {
return MatrixID.asAcceptable(record.getUid(), domain).getId();
} }
@Override @Override
@@ -67,71 +41,34 @@ public class GoogleFirebaseProvider extends GoogleFirebaseBackend implements ITh
return 25; return 25;
} }
private void waitOnLatch(CountDownLatch l) { private Optional<String> findInternal(String medium, String address) {
try { CompletableFuture<Optional<String>> future = new CompletableFuture<>();
l.await(30, TimeUnit.SECONDS);
} catch (InterruptedException e) {
log.warn("Interrupted while waiting for Firebase auth check");
}
}
private Optional<UserRecord> findInternal(String medium, String address) { // Directly complete with empty to simulate no user found
final UserRecord[] r = new UserRecord[1]; future.complete(Optional.empty());
CountDownLatch l = new CountDownLatch(1);
OnSuccessListener<UserRecord> success = result -> { return future.join(); // Using join to avoid handling InterruptedException
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]);
} }
@Override @Override
public Optional<SingleLookupReply> find(SingleLookupRequest request) { public Optional<SingleLookupReply> find(SingleLookupRequest request) {
Optional<UserRecord> urOpt = findInternal(request.getType(), request.getThreePid()); Optional<String> uidOpt = findInternal(request.getType(), request.getThreePid());
return urOpt.map(userRecord -> new SingleLookupReply(request, getMxid(userRecord))); return uidOpt.map(uid -> new SingleLookupReply(request, getMxid(uid)));
} }
@Override @Override
public List<ThreePidMapping> populate(List<ThreePidMapping> mappings) { public List<ThreePidMapping> populate(List<ThreePidMapping> mappings) {
List<ThreePidMapping> results = new ArrayList<>(); List<ThreePidMapping> results = new ArrayList<>();
mappings.parallelStream().forEach(o -> { mappings.forEach(o -> {
Optional<UserRecord> urOpt = findInternal(o.getMedium(), o.getValue()); Optional<String> uidOpt = findInternal(o.getMedium(), o.getValue());
if (urOpt.isPresent()) { uidOpt.ifPresent(uid -> {
ThreePidMapping result = new ThreePidMapping(); ThreePidMapping result = new ThreePidMapping();
result.setMedium(o.getMedium()); result.setMedium(o.getMedium());
result.setValue(o.getValue()); result.setValue(o.getValue());
result.setMxid(getMxid(urOpt.get())); result.setMxid(getMxid(uid));
results.add(result); results.add(result);
} });
}); });
return results; 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.config.ldap.LdapConfig;
import io.kamax.mxisd.exception.InternalServerError; import io.kamax.mxisd.exception.InternalServerError;
import io.kamax.mxisd.util.GsonUtil; 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.CursorException;
import org.apache.directory.api.ldap.model.cursor.CursorLdapReferralException; import org.apache.directory.api.ldap.model.cursor.CursorLdapReferralException;
import org.apache.directory.api.ldap.model.cursor.EntryCursor; 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); private transient final Logger log = LoggerFactory.getLogger(LdapAuthProvider.class);
public static final char[] CHARACTERS_TO_ESCAPE = ",#+<>;\"=*\\\\".toCharArray();
private PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance(); private PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
public LdapAuthProvider(LdapConfig cfg, MatrixConfig mxCfg) { public LdapAuthProvider(LdapConfig cfg, MatrixConfig mxCfg) {
@@ -94,7 +96,8 @@ public class LdapAuthProvider extends LdapBackend implements AuthenticatorProvid
return BackendAuthResult.failure(); return BackendAuthResult.failure();
} }
String userFilter = "(" + getUidAtt() + "=" + userFilterValue + ")"; String filteredValue = escape(userFilterValue);
String userFilter = "(" + getUidAtt() + "=" + filteredValue + ")";
userFilter = buildWithFilter(userFilter, getCfg().getAuth().getFilter()); userFilter = buildWithFilter(userFilter, getCfg().getAuth().getFilter());
Set<String> attributes = new HashSet<>(); Set<String> attributes = new HashSet<>();
@@ -162,8 +165,21 @@ public class LdapAuthProvider extends LdapBackend implements AuthenticatorProvid
log.info("No match were found for {}", mxid); log.info("No match were found for {}", mxid);
return BackendAuthResult.failure(); return BackendAuthResult.failure();
} catch (LdapException | IOException | CursorException e) { } catch (LdapException | IOException | CursorException e) {
log.error("Unable to invoke query request: ", e);
throw new InternalServerError(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; package io.kamax.mxisd.backend.ldap;
import io.kamax.matrix.MatrixID;
import io.kamax.matrix._MatrixID; import io.kamax.matrix._MatrixID;
import io.kamax.mxisd.config.MatrixConfig; import io.kamax.mxisd.config.MatrixConfig;
import io.kamax.mxisd.config.ldap.LdapConfig; 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.Attribute;
import org.apache.directory.api.ldap.model.entry.AttributeUtils; import org.apache.directory.api.ldap.model.entry.AttributeUtils;
import org.apache.directory.api.ldap.model.entry.Entry; import org.apache.directory.api.ldap.model.entry.Entry;
@@ -116,10 +117,20 @@ public abstract class LdapBackend {
public String buildMatrixIdFromUid(String uid) { public String buildMatrixIdFromUid(String uid) {
String uidType = getCfg().getAttribute().getUid().getType(); 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)) { 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)) { } else if (StringUtils.equals(MATRIX_ID, uidType)) {
return uid; return localpart;
} else { } else {
throw new IllegalArgumentException("Bind type " + uidType + " is not supported"); throw new IllegalArgumentException("Bind type " + uidType + " is not supported");
} }
@@ -128,6 +139,10 @@ public abstract class LdapBackend {
public String buildUidFromMatrixId(_MatrixID mxId) { public String buildUidFromMatrixId(_MatrixID mxId) {
String uidType = getCfg().getAttribute().getUid().getType(); String uidType = getCfg().getAttribute().getUid().getType();
if (StringUtils.equals(UID, uidType)) { if (StringUtils.equals(UID, uidType)) {
if(getCfg().isActiveDirectory()) {
return new UPN(mxId).getUPN();
}
return mxId.getLocalPart(); return mxId.getLocalPart();
} else if (StringUtils.equals(MATRIX_ID, uidType)) { } else if (StringUtils.equals(MATRIX_ID, uidType)) {
return mxId.getId(); return mxId.getId();
@@ -169,4 +184,58 @@ public abstract class LdapBackend {
return values; 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.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Collectors;
public class LdapThreePidProvider extends LdapBackend implements IThreePidProvider { public class LdapThreePidProvider extends LdapBackend implements IThreePidProvider {
@@ -137,4 +140,65 @@ public class LdapThreePidProvider extends LdapBackend implements IThreePidProvid
return mappingsFound; 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.ThreePidMapping;
import io.kamax.mxisd.lookup.provider.IThreePidProvider; import io.kamax.mxisd.lookup.provider.IThreePidProvider;
import io.kamax.mxisd.profile.ProfileProvider; 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.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -48,6 +48,7 @@ import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.stream.Collectors;
public class MemoryIdentityStore implements AuthenticatorProvider, DirectoryProvider, IThreePidProvider, ProfileProvider { public class MemoryIdentityStore implements AuthenticatorProvider, DirectoryProvider, IThreePidProvider, ProfileProvider {
@@ -171,4 +172,15 @@ public class MemoryIdentityStore implements AuthenticatorProvider, DirectoryProv
}).orElseGet(BackendAuthResult::failure); }).orElseGet(BackendAuthResult::failure);
} }
@Override
public Iterable<ThreePidMapping> populateHashes() {
if (!cfg.isHashEnabled()) {
return Collections.emptyList();
}
return cfg.getIdentities().stream()
.map(mic -> mic.getThreepids().stream().map(mtp -> new ThreePidMapping(mtp.getMedium(), mtp.getAddress(), mic.getUsername())))
.flatMap(s -> s).collect(
Collectors.toList());
}
} }

View File

@@ -27,7 +27,7 @@ import io.kamax.mxisd.lookup.SingleLookupReply;
import io.kamax.mxisd.lookup.SingleLookupRequest; import io.kamax.mxisd.lookup.SingleLookupRequest;
import io.kamax.mxisd.lookup.ThreePidMapping; import io.kamax.mxisd.lookup.ThreePidMapping;
import io.kamax.mxisd.lookup.provider.IThreePidProvider; 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.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -36,6 +36,7 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
@@ -104,4 +105,29 @@ public abstract class SqlThreePidProvider implements IThreePidProvider {
return new ArrayList<>(); return new ArrayList<>();
} }
@Override
public Iterable<ThreePidMapping> populateHashes() {
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(query);
try (ResultSet resultSet = statement.executeQuery()) {
while (resultSet.next()) {
String mxid = resultSet.getString("mxid");
String medium = resultSet.getString("medium");
String address = resultSet.getString("address");
result.add(new ThreePidMapping(medium, address, mxid));
}
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
return result;
}
} }

View File

@@ -28,7 +28,7 @@ import io.kamax.mxisd.config.sql.generic.GenericSqlProviderConfig;
import io.kamax.mxisd.directory.DirectoryProvider; import io.kamax.mxisd.directory.DirectoryProvider;
import io.kamax.mxisd.exception.InternalServerError; import io.kamax.mxisd.exception.InternalServerError;
import io.kamax.mxisd.http.io.UserDirectorySearchResult; 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.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;

View File

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

View File

@@ -21,7 +21,7 @@
package io.kamax.mxisd.backend.sql.synapse; package io.kamax.mxisd.backend.sql.synapse;
import io.kamax.mxisd.exception.ConfigurationException; import io.kamax.mxisd.exception.ConfigurationException;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.StringUtils;
public class SynapseQueries { public class SynapseQueries {
@@ -51,7 +51,7 @@ public class SynapseQueries {
if (StringUtils.equals("sqlite", type)) { if (StringUtils.equals("sqlite", type)) {
return "select " + getUserId(type, domain) + ", displayname from profiles p where displayname like ?"; return "select " + getUserId(type, domain) + ", displayname from profiles p where displayname like ?";
} else if (StringUtils.equals("postgresql", type)) { } 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 { } else {
throw new ConfigurationException("Invalid Synapse SQL type: " + type); throw new ConfigurationException("Invalid Synapse SQL type: " + type);
} }
@@ -72,7 +72,10 @@ public class SynapseQueries {
} }
public static String getRoomName() { 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.UserIdType;
import io.kamax.mxisd.auth.provider.AuthenticatorProvider; import io.kamax.mxisd.auth.provider.AuthenticatorProvider;
import io.kamax.mxisd.auth.provider.BackendAuthResult; 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.Logger;
import org.slf4j.LoggerFactory; 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.matrix.json.InvalidJsonException;
import io.kamax.mxisd.config.wordpress.WordpressConfig; import io.kamax.mxisd.config.wordpress.WordpressConfig;
import io.kamax.mxisd.util.RestClientUtils; 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.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpPost;

View File

@@ -0,0 +1,8 @@
package io.kamax.mxisd.config;
public enum AcceptingPolicy {
ALL,
ANY
}

View File

@@ -0,0 +1,24 @@
package io.kamax.mxisd.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class AccountConfig {
private final static Logger log = LoggerFactory.getLogger(DirectoryConfig.class);
private boolean allowOnlyTrustDomains = true;
public boolean isAllowOnlyTrustDomains() {
return allowOnlyTrustDomains;
}
public void setAllowOnlyTrustDomains(boolean allowOnlyTrustDomains) {
this.allowOnlyTrustDomains = allowOnlyTrustDomains;
}
public void build() {
log.info("--- Account config ---");
log.info("Allow registration only for trust domain: {}", isAllowOnlyTrustDomains());
}
}

View File

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

View File

@@ -0,0 +1,30 @@
package io.kamax.mxisd.config;
public class DurationDeserializer {
public long deserialize(String argument) {
long duration = 0L;
for (String part : argument.split(" ")) {
String unit = part.substring(part.length() - 1);
long value = Long.parseLong(part.substring(0, part.length() - 1));
switch (unit) {
case "s":
duration += value;
break;
case "m":
duration += value * 60;
break;
case "h":
duration += value * 60 * 60;
break;
case "d":
duration += value * 60 * 60 * 24;
break;
default:
throw new IllegalArgumentException(String.format("Unknown duration unit: %s", unit));
}
}
return duration;
}
}

View File

@@ -309,6 +309,7 @@ public class ExecConfig {
private Boolean enabled; private Boolean enabled;
private int priority; private int priority;
private Lookup lookup = new Lookup(); private Lookup lookup = new Lookup();
private boolean hashLookup = false;
public Boolean isEnabled() { public Boolean isEnabled() {
return enabled; return enabled;
@@ -334,6 +335,13 @@ public class ExecConfig {
this.lookup = lookup; this.lookup = lookup;
} }
public boolean isHashLookup() {
return hashLookup;
}
public void setHashLookup(boolean hashLookup) {
this.hashLookup = hashLookup;
}
} }
public static class Profile { public static class Profile {

View File

@@ -0,0 +1,125 @@
package io.kamax.mxisd.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
public class HashingConfig {
private static final Logger LOGGER = LoggerFactory.getLogger(HashingConfig.class);
private boolean enabled = false;
private int pepperLength = 20;
private RotationPolicyEnum rotationPolicy;
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(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());
LOGGER.info(" Rotation delay in seconds: {}", getDelayInSeconds());
}
if (RotationPolicyEnum.per_requests == getRotationPolicy()) {
LOGGER.info(" Rotation after requests: {}", getRequests());
}
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.");
}
}
public enum Algorithm {
none,
sha256
}
public enum RotationPolicyEnum {
per_requests,
per_seconds
}
public enum HashStorageEnum {
in_memory,
sql
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public int getPepperLength() {
return pepperLength;
}
public void setPepperLength(int pepperLength) {
this.pepperLength = pepperLength;
}
public RotationPolicyEnum getRotationPolicy() {
return rotationPolicy;
}
public void setRotationPolicy(RotationPolicyEnum rotationPolicy) {
this.rotationPolicy = rotationPolicy;
}
public HashStorageEnum getHashStorageType() {
return hashStorageType;
}
public void setHashStorageType(HashStorageEnum hashStorageType) {
this.hashStorageType = hashStorageType;
}
public String getDelay() {
return delay;
}
public void setDelay(String delay) {
this.delay = delay;
}
public long getDelayInSeconds() {
return delayInSeconds;
}
public void setDelayInSeconds(long delayInSeconds) {
this.delayInSeconds = delayInSeconds;
}
public int getRequests() {
return requests;
}
public void setRequests(int requests) {
this.requests = requests;
}
public List<Algorithm> getAlgorithms() {
return algorithms;
}
public void setAlgorithms(List<Algorithm> algorithms) {
this.algorithms = algorithms;
}
}

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 boolean recursive = true;
private long timer = 5; private long timer = 5;
private PeriodDimension period = PeriodDimension.minutes;
public boolean isRecursive() { public boolean isRecursive() {
return recursive; return recursive;
@@ -84,6 +85,13 @@ public class InvitationConfig {
this.timer = timer; this.timer = timer;
} }
public PeriodDimension getPeriod() {
return period;
}
public void setPeriod(PeriodDimension period) {
this.period = period;
}
} }
public static class SenderPolicy { public static class SenderPolicy {
@@ -115,6 +123,7 @@ public class InvitationConfig {
private Expiration expiration = new Expiration(); private Expiration expiration = new Expiration();
private Resolution resolution = new Resolution(); private Resolution resolution = new Resolution();
private Policies policy = new Policies(); private Policies policy = new Policies();
private boolean fullDisplayName = false;
public Expiration getExpiration() { public Expiration getExpiration() {
return expiration; return expiration;
@@ -140,11 +149,26 @@ public class InvitationConfig {
this.policy = policy; this.policy = policy;
} }
public boolean isFullDisplayName() {
return fullDisplayName;
}
public void setFullDisplayName(boolean fullDisplayName) {
this.fullDisplayName = fullDisplayName;
}
public void build() { public void build() {
log.info("--- Invite config ---"); log.info("--- Invite config ---");
log.info("Expiration: {}", GsonUtil.get().toJson(getExpiration())); log.info("Expiration: {}", GsonUtil.get().toJson(getExpiration()));
log.info("Resolution: {}", GsonUtil.get().toJson(getResolution())); log.info("Resolution: {}", GsonUtil.get().toJson(getResolution()));
log.info("Policies: {}", GsonUtil.get().toJson(getPolicy())); 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; package io.kamax.mxisd.config;
import io.kamax.mxisd.exception.ConfigurationException; import io.kamax.mxisd.exception.ConfigurationException;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.StringUtils;
public class KeyConfig { 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.matrix.json.GsonUtil;
import io.kamax.mxisd.exception.ConfigurationException; 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.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -63,6 +63,8 @@ public class MatrixConfig {
private String domain; private String domain;
private Identity identity = new Identity(); private Identity identity = new Identity();
private boolean v1 = true;
private boolean v2 = false;
public String getDomain() { public String getDomain() {
return domain; return domain;
@@ -80,6 +82,22 @@ public class MatrixConfig {
this.identity = identity; this.identity = identity;
} }
public boolean isV1() {
return v1;
}
public void setV1(boolean v1) {
this.v1 = v1;
}
public boolean isV2() {
return v2;
}
public void setV2(boolean v2) {
this.v2 = v2;
}
public void build() { public void build() {
log.info("--- Matrix config ---"); log.info("--- Matrix config ---");
@@ -90,6 +108,11 @@ public class MatrixConfig {
log.info("Domain: {}", getDomain()); log.info("Domain: {}", getDomain());
log.info("Identity:"); log.info("Identity:");
log.info("\tServers: {}", GsonUtil.get().toJson(identity.getServers())); log.info("\tServers: {}", GsonUtil.get().toJson(identity.getServers()));
log.info("API v1: {}", v1);
log.info("API v2: {}", v2);
if (v1) {
log.warn("API v1 is deprecated via MSC2140: https://github.com/matrix-org/matrix-doc/pull/2140 and will be deleted in future releases.");
log.warn("Please upgrade your homeserver and enable only API v2.");
}
} }
} }

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.ThreePidConfig;
import io.kamax.mxisd.config.threepid.notification.NotificationConfig; import io.kamax.mxisd.config.threepid.notification.NotificationConfig;
import io.kamax.mxisd.config.wordpress.WordpressConfig; 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.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -92,6 +92,7 @@ public class MxisdConfig {
private AppServiceConfig appsvc = new AppServiceConfig(); private AppServiceConfig appsvc = new AppServiceConfig();
private AuthenticationConfig auth = new AuthenticationConfig(); private AuthenticationConfig auth = new AuthenticationConfig();
private DirectoryConfig directory = new DirectoryConfig(); private DirectoryConfig directory = new DirectoryConfig();
private AccountConfig accountConfig = new AccountConfig();
private Dns dns = new Dns(); private Dns dns = new Dns();
private ExecConfig exec = new ExecConfig(); private ExecConfig exec = new ExecConfig();
private FirebaseConfig firebase = new FirebaseConfig(); private FirebaseConfig firebase = new FirebaseConfig();
@@ -114,6 +115,10 @@ public class MxisdConfig {
private ThreePidConfig threepid = new ThreePidConfig(); private ThreePidConfig threepid = new ThreePidConfig();
private ViewConfig view = new ViewConfig(); private ViewConfig view = new ViewConfig();
private WordpressConfig wordpress = new WordpressConfig(); 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() { public AppServiceConfig getAppsvc() {
return appsvc; return appsvc;
@@ -131,6 +136,14 @@ public class MxisdConfig {
this.auth = auth; this.auth = auth;
} }
public AccountConfig getAccountConfig() {
return accountConfig;
}
public void setAccountConfig(AccountConfig accountConfig) {
this.accountConfig = accountConfig;
}
public DirectoryConfig getDirectory() { public DirectoryConfig getDirectory() {
return directory; return directory;
} }
@@ -315,6 +328,30 @@ public class MxisdConfig {
this.wordpress = wordpress; this.wordpress = wordpress;
} }
public PolicyConfig getPolicy() {
return policy;
}
public void setPolicy(PolicyConfig policy) {
this.policy = policy;
}
public HashingConfig getHashing() {
return hashing;
}
public void setHashing(HashingConfig hashing) {
this.hashing = hashing;
}
public LoggingConfig getLogging() {
return logging;
}
public void setLogging(LoggingConfig logging) {
this.logging = logging;
}
public MxisdConfig inMemory() { public MxisdConfig inMemory() {
getKey().setPath(":memory:"); getKey().setPath(":memory:");
getStorage().getProvider().getSqlite().setDatabase(":memory:"); getStorage().getProvider().getSqlite().setDatabase(":memory:");
@@ -322,7 +359,17 @@ public class MxisdConfig {
return this; return this;
} }
public InternalAPIConfig getInternal() {
return internal;
}
public void setInternal(InternalAPIConfig internal) {
this.internal = internal;
}
public MxisdConfig build() { public MxisdConfig build() {
getLogging().build();
if (StringUtils.isBlank(getServer().getName())) { if (StringUtils.isBlank(getServer().getName())) {
getServer().setName(getMatrix().getDomain()); getServer().setName(getMatrix().getDomain());
log.debug("server.name is empty, using matrix.domain"); log.debug("server.name is empty, using matrix.domain");
@@ -330,7 +377,9 @@ public class MxisdConfig {
getAppsvc().build(); getAppsvc().build();
getAuth().build(); getAuth().build();
getAccountConfig().build();
getDirectory().build(); getDirectory().build();
getDns().build();
getExec().build(); getExec().build();
getFirebase().build(); getFirebase().build();
getForward().build(); getForward().build();
@@ -352,6 +401,9 @@ public class MxisdConfig {
getThreepid().build(); getThreepid().build();
getView().build(); getView().build();
getWordpress().build(); getWordpress().build();
getPolicy().build();
getHashing().build(getMatrix());
getInternal().build();
return this; return this;
} }

View File

@@ -0,0 +1,114 @@
package io.kamax.mxisd.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
public class PolicyConfig {
private static final Logger LOGGER = LoggerFactory.getLogger(PolicyConfig.class);
private Map<String, PolicyObject> policies = new HashMap<>();
public static class TermObject {
private String name;
private String url;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}
public static class PolicyObject {
private String version;
private Map<String, TermObject> terms;
private List<String> regexp = new ArrayList<>();
private transient List<Pattern> patterns = new ArrayList<>();
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public Map<String, TermObject> getTerms() {
return terms;
}
public void setTerms(Map<String, TermObject> terms) {
this.terms = terms;
}
public List<String> getRegexp() {
return regexp;
}
public void setRegexp(List<String> regexp) {
this.regexp = regexp;
}
public List<Pattern> getPatterns() {
return patterns;
}
}
public Map<String, PolicyObject> getPolicies() {
return policies;
}
public void setPolicies(Map<String, PolicyObject> policies) {
this.policies = policies;
}
public void build() {
LOGGER.info("--- Policy Config ---");
if (getPolicies().isEmpty()) {
LOGGER.info("Empty");
} else {
for (Map.Entry<String, PolicyObject> policyObjectItem : getPolicies().entrySet()) {
PolicyObject policyObject = policyObjectItem.getValue();
StringBuilder sb = new StringBuilder();
sb.append("Policy \"").append(policyObjectItem.getKey()).append("\"\n");
sb.append(" version: ").append(policyObject.getVersion()).append("\n");
for (String regexp : policyObjectItem.getValue().getRegexp()) {
sb.append(" - ").append(regexp).append("\n");
policyObjectItem.getValue().getPatterns().add(Pattern.compile(regexp));
}
sb.append(" terms:\n");
if (policyObject.getTerms() != null) {
for (Map.Entry<String, TermObject> termItem : policyObject.getTerms().entrySet()) {
sb.append(" - lang: ").append(termItem.getKey()).append("\n");
sb.append(" name: ").append(termItem.getValue().getName()).append("\n");
sb.append(" url: ").append(termItem.getValue().getUrl()).append("\n");
}
}
LOGGER.info(sb.toString());
}
}
}
}

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; package io.kamax.mxisd.config;
public class SQLiteStorageConfig { public class SQLiteStorageConfig implements DatabaseStorageConfig {
private String database; private String database;
@Override
public String getDatabase() { public String getDatabase() {
return database; return database;
} }

View File

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

View File

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

View File

@@ -21,7 +21,7 @@
package io.kamax.mxisd.config; package io.kamax.mxisd.config;
import io.kamax.matrix.json.GsonUtil; 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.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;

View File

@@ -1,39 +1,15 @@
/*
* 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; package io.kamax.mxisd.config;
import io.kamax.matrix.json.GsonUtil;
import io.kamax.mxisd.exception.ConfigurationException;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.Constructor; import org.yaml.snakeyaml.constructor.Constructor;
import org.yaml.snakeyaml.introspector.BeanAccess; import org.yaml.snakeyaml.LoaderOptions;
import org.yaml.snakeyaml.parser.ParserException;
import org.yaml.snakeyaml.representer.Representer;
import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileNotFoundException; import java.io.FileWriter;
import java.io.IOException; import java.io.IOException;
import java.io.File;
import java.util.Optional; import java.util.Optional;
public class YamlConfigLoader { public class YamlConfigLoader {
@@ -41,39 +17,41 @@ public class YamlConfigLoader {
private static final Logger log = LoggerFactory.getLogger(YamlConfigLoader.class); private static final Logger log = LoggerFactory.getLogger(YamlConfigLoader.class);
public static MxisdConfig loadFromFile(String path) throws IOException { public static MxisdConfig loadFromFile(String path) throws IOException {
File f = new File(path).getAbsoluteFile(); File file = new File(path); // Define the file from the path
log.info("Reading config from {}", f.toString()); Constructor constructor = new Constructor(MxisdConfig.class); // Ensure correct import
Representer rep = new Representer(); Yaml yaml = new Yaml(constructor); // No change needed here, this is correct
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);
// SnakeYaml set objects to null when there is no value set in the config, even a full sub-tree. // Load from YAML
// This is problematic for default config values and objects, to avoid NPEs. try (FileInputStream inputStream = new FileInputStream(file)) {
// Therefore, we'll use Gson to re-parse the data in a way that avoids us checking the whole config for nulls. return yaml.loadAs(inputStream, MxisdConfig.class);
MxisdConfig cfg = GsonUtil.get().fromJson(GsonUtil.get().toJson(raw), MxisdConfig.class); } catch (IOException e) {
// Handle exceptions
log.info("Loaded config from {}", path); throw e;
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");
} }
} }
public static Optional<MxisdConfig> tryLoadFromFile(String path) { public static Optional<MxisdConfig> tryLoadFromFile(String path) {
log.debug("Attempting to read config from {}", path);
try { try {
return Optional.of(loadFromFile(path)); return Optional.of(loadFromFile(path));
} catch (FileNotFoundException e) {
log.info("No config file at {}", path);
return Optional.empty();
} catch (IOException e) { } 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.matrix.json.GsonUtil;
import io.kamax.mxisd.backend.ldap.LdapBackend; import io.kamax.mxisd.backend.ldap.LdapBackend;
import io.kamax.mxisd.exception.ConfigurationException; 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.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -233,6 +233,7 @@ public abstract class LdapConfig {
private String filter; private String filter;
private String token = "%3pid"; private String token = "%3pid";
private Map<String, String> medium = new HashMap<>(); private Map<String, String> medium = new HashMap<>();
private boolean lookup = false;
public String getFilter() { public String getFilter() {
return filter; return filter;
@@ -262,6 +263,13 @@ public abstract class LdapConfig {
this.medium = medium; this.medium = medium;
} }
public boolean isLookup() {
return lookup;
}
public void setLookup(boolean lookup) {
this.lookup = lookup;
}
} }
public static class Profile { public static class Profile {
@@ -283,6 +291,9 @@ public abstract class LdapConfig {
private boolean enabled; private boolean enabled;
private String filter; private String filter;
private boolean activeDirectory;
private String defaultDomain;
private Connection connection = new Connection(); private Connection connection = new Connection();
private Attribute attribute = new Attribute(); private Attribute attribute = new Attribute();
private Auth auth = new Auth(); private Auth auth = new Auth();
@@ -308,6 +319,22 @@ public abstract class LdapConfig {
this.filter = filter; 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() { public Connection getConnection() {
return connection; return connection;
} }
@@ -399,6 +426,15 @@ public abstract class LdapConfig {
throw new ConfigurationException("ldap.identity.token"); 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 // Build queries
attribute.getThreepid().forEach((k, v) -> { attribute.getThreepid().forEach((k, v) -> {
if (StringUtils.isBlank(identity.getMedium().get(k))) { if (StringUtils.isBlank(identity.getMedium().get(k))) {

View File

@@ -27,6 +27,7 @@ public class MemoryStoreConfig {
private boolean enabled; private boolean enabled;
private List<MemoryIdentityConfig> identities = new ArrayList<>(); private List<MemoryIdentityConfig> identities = new ArrayList<>();
private boolean hashEnabled = false;
public boolean isEnabled() { public boolean isEnabled() {
return enabled; return enabled;
@@ -44,6 +45,14 @@ public class MemoryStoreConfig {
this.identities = identities; this.identities = identities;
} }
public boolean isHashEnabled() {
return hashEnabled;
}
public void setHashEnabled(boolean hashEnabled) {
this.hashEnabled = hashEnabled;
}
public void build() { public void build() {
// no-op // no-op
} }

View File

@@ -45,4 +45,21 @@ public class MemoryThreePid implements _ThreePid {
this.address = address; 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; package io.kamax.mxisd.config.rest;
import io.kamax.mxisd.exception.ConfigurationException; 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.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -33,8 +33,8 @@ public class RestBackendConfig {
public static class IdentityEndpoints { public static class IdentityEndpoints {
private String single = "/_ma1sd/backend/api/v1/identity/single"; private String single = "/_mxids/backend/api/v1/identity/single";
private String bulk = "/_ma1sd/backend/api/v1/identity/bulk"; private String bulk = "/_mxids/backend/api/v1/identity/bulk";
public String getSingle() { public String getSingle() {
return single; return single;
@@ -56,9 +56,9 @@ public class RestBackendConfig {
public static class ProfileEndpoints { public static class ProfileEndpoints {
private String displayName = "/_ma1sd/backend/api/v1/profile/displayName"; private String displayName = "/_mxids/backend/api/v1/profile/displayName";
private String threepids = "/_ma1sd/backend/api/v1/profile/threepids"; private String threepids = "/_mxids/backend/api/v1/profile/threepids";
private String roles = "/_ma1sd/backend/api/v1/profile/roles"; private String roles = "/_mxids/backend/api/v1/profile/roles";
public String getDisplayName() { public String getDisplayName() {
return displayName; return displayName;
@@ -88,8 +88,8 @@ public class RestBackendConfig {
public static class Endpoints { public static class Endpoints {
private String auth = "/_ma1sd/backend/api/v1/auth/login"; private String auth = "/_mxids/backend/api/v1/auth/login";
private String directory = "/_ma1sd/backend/api/v1/directory/user/search"; private String directory = "/_mxids/backend/api/v1/directory/user/search";
private IdentityEndpoints identity = new IdentityEndpoints(); private IdentityEndpoints identity = new IdentityEndpoints();
private ProfileEndpoints profile = new ProfileEndpoints(); private ProfileEndpoints profile = new ProfileEndpoints();

View File

@@ -124,11 +124,23 @@ public abstract class SqlConfig {
} }
public static class Lookup {
private String query = "SELECT user_id AS mxid, medium, address from user_threepid_id_server";
public String getQuery() {
return query;
}
public void setQuery(String query) {
this.query = query;
}
}
public static class Identity { public static class Identity {
private Boolean enabled; private Boolean enabled;
private String type = "mxid"; 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<>(); private Map<String, String> medium = new HashMap<>();
public Boolean isEnabled() { public Boolean isEnabled() {
@@ -264,6 +276,7 @@ public abstract class SqlConfig {
private Directory directory = new Directory(); private Directory directory = new Directory();
private Identity identity = new Identity(); private Identity identity = new Identity();
private Profile profile = new Profile(); private Profile profile = new Profile();
private Lookup lookup = new Lookup();
public boolean isEnabled() { public boolean isEnabled() {
return enabled; return enabled;
@@ -321,6 +334,14 @@ public abstract class SqlConfig {
this.profile = profile; this.profile = profile;
} }
public Lookup getLookup() {
return lookup;
}
public void setLookup(Lookup lookup) {
this.lookup = lookup;
}
protected abstract String getProviderName(); protected abstract String getProviderName();
public void build() { public void build() {
@@ -354,6 +375,7 @@ public abstract class SqlConfig {
log.info("Identity type: {}", getIdentity().getType()); log.info("Identity type: {}", getIdentity().getType());
log.info("3PID mapping query: {}", getIdentity().getQuery()); log.info("3PID mapping query: {}", getIdentity().getQuery());
log.info("Identity medium queries: {}", GsonUtil.build().toJson(getIdentity().getMedium())); log.info("Identity medium queries: {}", GsonUtil.build().toJson(getIdentity().getMedium()));
log.info("Lookup query: {}", getLookup().getQuery());
log.info("Profile:"); log.info("Profile:");
log.info(" Enabled: {}", getProfile().isEnabled()); log.info(" Enabled: {}", getProfile().isEnabled());
if (getProfile().isEnabled()) { if (getProfile().isEnabled()) {

View File

@@ -23,10 +23,24 @@ package io.kamax.mxisd.config.sql.synapse;
import io.kamax.mxisd.UserIdType; import io.kamax.mxisd.UserIdType;
import io.kamax.mxisd.backend.sql.synapse.SynapseQueries; import io.kamax.mxisd.backend.sql.synapse.SynapseQueries;
import io.kamax.mxisd.config.sql.SqlConfig; 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 { 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 @Override
protected String getProviderName() { protected String getProviderName() {
return "Synapse SQL"; return "Synapse SQL";
@@ -42,7 +56,7 @@ public class SynapseSqlProviderConfig extends SqlConfig {
if (getIdentity().isEnabled() && StringUtils.isBlank(getIdentity().getType())) { if (getIdentity().isEnabled() && StringUtils.isBlank(getIdentity().getType())) {
getIdentity().setType("mxid"); 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()) { if (getProfile().isEnabled()) {
@@ -65,4 +79,12 @@ public class SynapseSqlProviderConfig extends SqlConfig {
printConfig(); 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; package io.kamax.mxisd.config.threepid.connector;
import io.kamax.mxisd.util.GsonUtil; 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.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;

View File

@@ -20,7 +20,7 @@
package io.kamax.mxisd.config.threepid.connector; 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.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;

View File

@@ -20,7 +20,7 @@
package io.kamax.mxisd.config.threepid.medium; 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.HashMap;
import java.util.Map; import java.util.Map;

View File

@@ -21,7 +21,7 @@
package io.kamax.mxisd.config.wordpress; package io.kamax.mxisd.config.wordpress;
import io.kamax.mxisd.exception.ConfigurationException; 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.HashMap;
import java.util.Map; 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.http.io.UserDirectorySearchResult;
import io.kamax.mxisd.util.RestClientUtils; import io.kamax.mxisd.util.RestClientUtils;
import org.apache.commons.io.IOUtils; 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.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder; import org.apache.http.client.utils.URIBuilder;

View File

@@ -0,0 +1,28 @@
/*
* 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.exception;
public class InvalidParamException extends RuntimeException {
public InvalidParamException() {
super("The chosen hash algorithm is invalid or disallowed");
}
}

View File

@@ -0,0 +1,28 @@
/*
* 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.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

@@ -0,0 +1,94 @@
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;
import io.kamax.mxisd.hash.rotation.TimeBasedRotation;
import io.kamax.mxisd.hash.storage.EmptyStorage;
import io.kamax.mxisd.hash.storage.HashStorage;
import io.kamax.mxisd.hash.storage.InMemoryHashStorage;
import io.kamax.mxisd.hash.storage.SqlHashStorage;
import io.kamax.mxisd.lookup.provider.IThreePidProvider;
import io.kamax.mxisd.storage.IStorage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
public class HashManager {
private static final Logger LOGGER = LoggerFactory.getLogger(HashManager.class);
private Engine engine;
private HashRotationStrategy rotationStrategy;
private HashStorage hashStorage;
private HashingConfig config;
private IStorage storage;
private AtomicBoolean configured = new AtomicBoolean(false);
public void init(HashingConfig config, List<? extends IThreePidProvider> providers, IStorage storage) {
this.config = config;
this.storage = storage;
initStorage();
engine = config.isEnabled() ? new HashEngine(providers, getHashStorage(), config) : new NoneEngine();
initRotationStrategy();
configured.set(true);
}
private void initStorage() {
if (config.isEnabled()) {
switch (config.getHashStorageType()) {
case in_memory:
this.hashStorage = new InMemoryHashStorage();
break;
case sql:
this.hashStorage = new SqlHashStorage(storage);
break;
default:
throw new IllegalArgumentException("Unknown storage type: " + config.getHashStorageType());
}
} else {
this.hashStorage = new EmptyStorage();
}
}
private void initRotationStrategy() {
if (config.isEnabled()) {
switch (config.getRotationPolicy()) {
case per_requests:
this.rotationStrategy = new RotationPerRequests(config.getRequests());
break;
case per_seconds:
this.rotationStrategy = new TimeBasedRotation(config.getDelayInSeconds());
break;
default:
throw new IllegalArgumentException("Unknown rotation type: " + config.getHashStorageType());
}
} else {
this.rotationStrategy = new NoOpRotationStrategy();
}
this.rotationStrategy.register(getHashEngine());
}
public Engine getHashEngine() {
return engine;
}
public HashRotationStrategy getRotationStrategy() {
return rotationStrategy;
}
public HashStorage getHashStorage() {
return hashStorage;
}
public HashingConfig getConfig() {
return config;
}
}

View File

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

View File

@@ -0,0 +1,66 @@
package io.kamax.mxisd.hash.engine;
import io.kamax.mxisd.config.HashingConfig;
import io.kamax.mxisd.hash.storage.HashStorage;
import io.kamax.mxisd.lookup.ThreePidMapping;
import io.kamax.mxisd.lookup.provider.IThreePidProvider;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Base64;
import java.util.List;
public class HashEngine implements Engine {
private static final Logger LOGGER = LoggerFactory.getLogger(HashEngine.class);
private final List<? extends IThreePidProvider> providers;
private final HashStorage hashStorage;
private final HashingConfig config;
private final Base64.Encoder base64 = Base64.getUrlEncoder().withoutPadding();
private String pepper;
public HashEngine(List<? extends IThreePidProvider> providers, HashStorage hashStorage, HashingConfig config) {
this.providers = providers;
this.hashStorage = hashStorage;
this.config = config;
}
@Override
public void updateHashes() {
LOGGER.info("Start update hashes.");
synchronized (hashStorage) {
this.pepper = newPepper();
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) {
LOGGER.error("Unable to update hashes of the provider: " + provider.toString(), e);
}
}
}
LOGGER.info("Finish update hashes.");
}
@Override
public String getPepper() {
synchronized (hashStorage) {
return pepper;
}
}
protected String hash(ThreePidMapping pidMapping) {
return base64.encodeToString(DigestUtils.sha256(pidMapping.getValue() + " " + pidMapping.getMedium() + " " + getPepper()));
}
protected String newPepper() {
return RandomStringUtils.random(config.getPepperLength(), true, true);
}
}

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

@@ -0,0 +1,16 @@
package io.kamax.mxisd.hash.rotation;
import io.kamax.mxisd.hash.engine.Engine;
public interface HashRotationStrategy {
void register(Engine engine);
Engine getHashEngine();
void newRequest();
default void trigger() {
getHashEngine().updateHashes();
}
}

View File

@@ -0,0 +1,23 @@
package io.kamax.mxisd.hash.rotation;
import io.kamax.mxisd.hash.engine.Engine;
public class NoOpRotationStrategy implements HashRotationStrategy {
private Engine engine;
@Override
public void register(Engine engine) {
this.engine = engine;
}
@Override
public Engine getHashEngine() {
return engine;
}
@Override
public void newRequest() {
// nothing to do
}
}

View File

@@ -0,0 +1,36 @@
package io.kamax.mxisd.hash.rotation;
import io.kamax.mxisd.hash.engine.Engine;
import java.util.concurrent.atomic.AtomicInteger;
public class RotationPerRequests implements HashRotationStrategy {
private Engine engine;
private final AtomicInteger counter = new AtomicInteger(0);
private final int barrier;
public RotationPerRequests(int barrier) {
this.barrier = barrier;
}
@Override
public void register(Engine engine) {
this.engine = engine;
trigger();
}
@Override
public Engine getHashEngine() {
return engine;
}
@Override
public synchronized void newRequest() {
int newValue = counter.incrementAndGet();
if (newValue >= barrier) {
counter.set(0);
trigger();
}
}
}

View File

@@ -0,0 +1,34 @@
package io.kamax.mxisd.hash.rotation;
import io.kamax.mxisd.hash.engine.Engine;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class TimeBasedRotation implements HashRotationStrategy {
private final long delay;
private Engine engine;
private final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
public TimeBasedRotation(long delay) {
this.delay = delay;
}
@Override
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 Engine getHashEngine() {
return engine;
}
@Override
public void newRequest() {
}
}

View File

@@ -0,0 +1,25 @@
package io.kamax.mxisd.hash.storage;
import io.kamax.mxisd.lookup.ThreePidMapping;
import org.apache.commons.lang3.tuple.Pair;
import java.util.Collection;
import java.util.Collections;
public class EmptyStorage implements HashStorage {
@Override
public Collection<Pair<String, ThreePidMapping>> find(Iterable<String> hashes) {
return Collections.emptyList();
}
@Override
public void add(ThreePidMapping pidMapping, String hash) {
}
@Override
public void clear() {
}
}

View File

@@ -0,0 +1,15 @@
package io.kamax.mxisd.hash.storage;
import io.kamax.mxisd.lookup.ThreePidMapping;
import org.apache.commons.lang3.tuple.Pair;
import java.util.Collection;
public interface HashStorage {
Collection<Pair<String, ThreePidMapping>> find(Iterable<String> hashes);
void add(ThreePidMapping pidMapping, String hash);
void clear();
}

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