Compare commits

...

139 Commits

Author SHA1 Message Date
Anatoly Sablin
a112a5e57c Improve request verification. Allow unbind only for configured matrix domain. 2019-08-07 21:47:10 +03:00
Anatoly Sablin
dbc764fe65 Add description about the MSC1915 configuration. 2019-08-05 22:15:53 +03:00
Anatoly Sablin
d5680b2dfe MSC1915. Add the option to enable/disable unbind. 2019-07-31 23:22:21 +03:00
Anatoly Sablin
5aad4fb81e MSC1915. Add unbind email notification. 2019-07-31 00:13:44 +03:00
Anatoly Sablin
a1f64f5159 Reworked MSC1915. Add request validation. 2019-07-27 15:51:01 +03:00
Anatoly Sablin
a96920f533 Add migration guide. 2019-07-21 23:44:42 +03:00
Anatoly Sablin
9ca2ebdad0 Bump dependencies. 2019-07-21 18:25:35 +03:00
Anatoly Sablin
7f284ac71e Add gradle-version-plugin to track dependencies updates. 2019-07-21 18:25:35 +03:00
Anatoly Sablin
4d488a3fc8 Add openjdk-11 dependency. 2019-07-21 18:25:35 +03:00
Anatoly Sablin
7ee3e19de4 Change app name in logs. 2019-07-21 17:48:52 +03:00
Anatoly Sablin
608938c084 Fix links. 2019-07-16 00:14:57 +03:00
Anatoly Sablin
e6fec9199d Rename config file, parameters, application name, package. 2019-07-11 22:26:20 +03:00
Anatoly Sablin
c3262a9f25 Merge the matrix-java-sdk due to it no longer maintained. Remove thirdparty repositories. 2019-07-07 23:13:59 +03:00
Anatoly Sablin
136563c61a Rename project. 2019-06-28 22:50:31 +03:00
Anatoly Sablin
3043cd4e61 Fork! 2019-06-27 00:07:44 +03:00
Max Dor
21d9d0fda1 The End 2019-06-25 11:36:32 +02:00
Max Dor
a964b073bf Restart mxisd service on Debian package upgrade/install if possible 2019-06-12 00:18:25 +02:00
Max Dor
f85345bc97 Update code and links following Matrix 1.0 release
- Support 3PID unbind via 3PID sessions
2019-06-12 00:17:43 +02:00
Max Dor
29603682e5 Clarify how to serve static assets for 3PID session views 2019-06-04 17:06:25 +02:00
Max Dor
d54f1dcb88 Fix various typos in the Registration feature docs 2019-05-30 17:29:47 +02:00
Max Dor
92f10347d1 Fix #123 2019-05-30 14:18:11 +02:00
Max Dor
0298f66212 Fix #128 2019-05-30 13:58:40 +02:00
Max Dor
0ddd086bda Fix response body of /3pid/bind to match spec
- synapse did not check/validate the response as per spec until 0.99.5 it seems
- mxisd was never compliant also
2019-05-30 13:26:38 +02:00
Max Dor
544f8e59f0 Add check for legality of the returned Matrix ID in Auth
- Helps troubleshoot reported issues that might not be obvious at first
- Add basic unit test for auth manager
2019-05-28 19:28:46 +02:00
Max Dor
917f87bf8c Fix broken HTML tag in 3PID template 2019-05-28 16:01:01 +02:00
Max Dor
774795c203 Fix various logging/variable scopes 2019-05-27 17:12:52 +02:00
Max Dor
27b2976e42 Provide URL encoded placeholders in notification template for 3PID data 2019-05-18 02:20:13 +02:00
Max Dor
f16f184253 Minor internal changes
- Fix log statement to include expected value
- Change access level to method
2019-05-18 01:57:40 +02:00
Max Dor
cd890d114a Add warning about possibly unresolvable 3PID invites 2019-05-14 00:49:07 +02:00
Max Dor
321ba1e325 Code formatting (cosmetic, no-op) 2019-05-14 00:39:12 +02:00
Max Dor
c3ce0a17f6 Avoid conflict between 3PID expired user and Matrix ID users event 2019-05-13 16:08:35 +02:00
Max Dor
0fcc0d9bb2 Properly inform about bad configuration for 3PID builtin configs 2019-05-13 14:04:11 +02:00
Max Dor
ce7f900543 Make various optimisations/clarifications
- Change some log levels to be less verbose
- Add privacy link
- Remove unused code
2019-05-06 23:28:38 +02:00
Max Dor
c7c009f9af Fix indentation in builtin 3PID templates (cosmetic) 2019-05-06 19:16:20 +02:00
Max Dor
3b01663245 Switch to Gradle 5 build 2019-05-05 15:56:51 +02:00
Max Dor
9cc601d582 Fix custom config for custom notification handlers 2019-05-05 13:54:12 +02:00
Max Dor
e6272b1827 Improve detection and fast-fail on empty Sendgrid template paths 2019-05-05 13:48:14 +02:00
Max Dor
8243354f39 Remove unused but bug-triggering code block (Fix #172) 2019-05-04 11:17:36 +02:00
Max Dor
25968e0737 Log denied requests due to invalid credentials in AS 2019-05-04 11:16:19 +02:00
Max Dor
44a80461a0 Ensure lookup signatures are produced in a consistent way 2019-04-28 08:55:23 +02:00
Max Dor
85d9f9e704 Fix missing return in Homeserver endpoint discovery, skipping DNS SRV 2019-04-27 20:54:02 +02:00
Max Dor
6278301672 Document mxisd does not require any maintenance task for day-to-day operations 2019-04-27 17:34:56 +02:00
Max Dor
5ed0c66cfd Improve logging
- Give means to increase logging verbosity
- Explain how to do in the troubleshooting guide
2019-04-27 17:26:38 +02:00
Max Dor
ea58b6985a Fix LDAP default attributes dead link (Fix #136) 2019-04-27 16:39:36 +02:00
Max Dor
a44f781495 Properly encode Email notification headers (Fix #137) 2019-04-27 16:36:55 +02:00
Max Dor
0d42ee695a Support new Homeserver federation discovery with well-known (Fix #127) 2019-04-27 11:11:06 +02:00
Max Dor
f331af0941 Add various Notification template generator improvements
- Add ability to set arbitrary value for some placeholders (Fix #133)
- More Unit tests
- Improve doc
2019-04-27 01:07:44 +02:00
Max Dor
e2c8a56135 Allow for full TLS/SSL in SMTP connector (Fix #125) 2019-04-26 09:58:46 +02:00
Max Dor
a67c5d7ae1 Improve documentation about the SQL Identity store (Fix #107) 2019-04-26 09:40:38 +02:00
Max Dor
80352070f1 Add documentation for installation hardening and operations guide (Fix #140) 2019-04-26 09:14:16 +02:00
Max Dor
39447b8b8b Fix handling various GET and POST content types/logic for submitToken
- Properly support Form-encoded POST
- Fix #167
2019-04-26 08:41:06 +02:00
Max Dor
9d4680f55a Fix Twilio config docs to match parsed keys (v1.3.x regression) 2019-04-23 07:00:43 +02:00
Max Dor
d1ea0fbf0f Reflect default AppSvc feature enable status in config 2019-04-15 02:29:42 +02:00
Max Dor
ee21f051fb Merge branch 'to-v1.4' 2019-04-09 14:50:45 +02:00
Max Dor
6cc17abf2c Further document new features 2019-04-09 12:06:13 +02:00
Max Dor
a7b5accd75 Adapt AS doc to new format and capabilities 2019-04-09 02:50:58 +02:00
Max Dor
6bb0c93f57 Fix typo 2019-04-05 21:56:05 +02:00
Max Dor
9abdcc15ba Clarify specifics about synapse identity store 2019-04-03 00:50:48 +02:00
Max Dor
eb903bf226 Document new 3PID invite expiration feature 2019-04-03 00:44:30 +02:00
Max Dor
1cbb0a135b Add doc about new registration control feature 2019-04-02 11:56:48 +02:00
Joshua M. Boniface
1587103c0a Add Section and Priority Debian control fields (#150) 2019-04-01 03:01:10 +02:00
Max Dor
838d79ae15 Remove mention to the community Identity room 2019-03-11 19:46:23 +01:00
Max Dor
96c47ecf76 Merge pull request #143 from c7hm4r/patch-1
Fix typo in example configuration
2019-03-07 21:11:40 +01:00
Christoph Müller
c5cea933a4 Fix typo in example configuration 2019-03-07 21:07:40 +01:00
Max Dor
57c7e4a91d Show signatures into admin lookup queries 2019-03-04 02:12:55 +01:00
Max Dor
1dce59a02e Add lookup and invite commands to the admin AS interface 2019-03-04 00:02:13 +01:00
Max Dor
de840b9d00 Skeleton for modular AS admin command processing 2019-03-03 16:39:58 +01:00
Max Dor
53c85d2248 Package/Class refactoring (no-op) 2019-03-03 03:44:38 +01:00
Max Dor
254dc5684f Add mechanisms for 3PID invite expiration and AS integration
- Integration with AS and a fallback user to decline expired invites (#120)
- Rework of the AS feature to make it more independent/re-usable
- Skeleton for admin interface via bot to manage invites (#138)
2019-03-02 03:21:29 +01:00
Max Dor
de92e98f7d Save work in progress 2019-03-01 17:51:33 +01:00
Max Dor
d5f9137056 split into app svc processor 2019-03-01 15:58:37 +01:00
Max Dor
1307e3aa43 Add missing javadoc 2019-03-01 15:18:47 +01:00
Max Dor
dfedde0df6 Improve crypto
- Re-organize packages to be consistent
- Add Key store tests
2019-03-01 15:16:19 +01:00
Max Dor
93bd7354c2 Improve Authentication doc 2019-03-01 12:42:13 +01:00
Max Dor
c302789898 Add mechanism for 3PID invites expiration (#120) 2019-03-01 06:51:18 +01:00
Max Dor
96155c1876 Improving logging 2019-03-01 01:12:02 +01:00
Max Dor
95ee328281 Block custom internal endpoint that should never be called
- Is not spec'd
- Will not be spec'd
- Is 100% internal as per its authors
2019-02-25 14:06:32 +01:00
Max Dor
72a1794cc3 Skeleton for 3PID registration policies (#130) 2019-02-18 23:08:50 +01:00
Max Dor
37ddd0e588 Talk about server.name in the example config 2019-02-17 03:22:48 +01:00
Max Dor
4d63bba251 Add version in jar
- Cli argument
- In HTTP client
- /version endpoint
2019-02-17 02:08:50 +01:00
Max Dor
aadfae2965 Skeleton for invitation policies (#130) 2019-02-17 02:08:50 +01:00
Max Dor
2f7e5e4025 Fix migration in case of empty dir 2019-02-17 02:08:50 +01:00
Max Dor
77dc75d383 Basic check for pending invite when requesting token on registration 2019-02-17 02:08:50 +01:00
Max Dor
f3b528d1ba Store ephemeral key in invite and add support for /sign-ed25519 2019-02-17 02:08:50 +01:00
Max Dor
91e5e08e70 Support for all key types 2019-02-17 02:08:50 +01:00
Max Dor
acd8c7d7c5 Skeleton for full support of all key types 2019-02-17 02:08:50 +01:00
Max Dor
249cc0ea92 Improve troubleshooting doc/flows
- Use better wording for unknown server error
- Add basic troubleshooting doc
2019-02-17 02:06:13 +01:00
Max Dor
99697d7c75 Various doc fixes and improvements 2019-02-14 00:39:33 +01:00
Max Dor
e133e120d7 Fix Exec store breakage following change to new config format 2019-02-13 21:08:56 +01:00
Max Dor
e39d6bfa10 Better handling of YAML->Java object config processing 2019-02-13 21:08:35 +01:00
Max Dor
217bc423ed Fix edge case of error when parsing valid config for directory 2019-02-13 20:19:26 +01:00
Max Dor
8f0654c34e Fix oversight in potentially printing credentials to log 2019-02-13 12:40:01 +01:00
Max Dor
8afdb3ed83 Improve feedback in case of parsing error in config file 2019-02-11 03:18:50 +01:00
Max Dor
bd4ccbc5e5 Fix some edge cases configuration parsing
- Optional in getter but not in setter seems problematic
- Document config parsing better
- Properly handle empty values in REST Profile so no HTTP call is made
- Possibly related to #113
2019-02-11 02:56:02 +01:00
Max Dor
6d1c6ed109 Last cosmetic changes for v1.3.0 2019-02-10 20:41:40 +01:00
Max Dor
1619f5311c Add email verification notification test (/requestToken) 2019-02-09 15:18:06 +01:00
Max Dor
6fa36ea092 Add missing header 2019-02-07 01:39:10 +01:00
Max Dor
471e06536b Improve logging 2019-02-07 01:35:43 +01:00
Max Dor
3a6b75996c Use a proper HTTP client when discovering federated IS to avoid 4xx's 2019-02-06 23:23:40 +01:00
Max Dor
566e4f3137 Correctly handle 3PID notification revamping (forgotten code) 2019-02-06 22:27:42 +01:00
Max Dor
a4c18dee5d Handle possibly trailing slashes for older versions of mxisd 2019-02-06 19:55:22 +01:00
Max Dor
8d6850d346 Link to targeted setups in main README 2019-02-06 04:03:33 +01:00
Max Dor
67bc18af7d Improve docs 2019-02-06 03:53:42 +01:00
Max Dor
5c660fdcaf Add forgotten CORS headers from Spring port 2019-02-05 19:09:47 +01:00
Max Dor
fbbafeb769 Cache processing of bulk lookups and de-dup concurrent requests 2019-02-04 06:04:39 +01:00
Max Dor
559f6a7401 Fix docs 2019-02-04 06:03:15 +01:00
Max Dor
3bebb33147 Revamp 3PID sessions
- Fix #93
- Fix #98
2019-02-04 05:26:33 +01:00
Max Dor
3e240fe34d Improve fraudulent unbind notification 2019-02-01 15:41:44 +01:00
Max Dor
635f6fdbe7 Implementation for blocking fraudulent 3PID /unbind attempts 2019-02-01 02:34:52 +01:00
Max Dor
4237eeb3b6 Skeleton for blocking fraudulent 3PID /unbind attempts 2019-01-30 00:29:51 +01:00
Max Dor
a0e91e7896 Use proper return codes for session errors 2019-01-30 00:28:55 +01:00
Max Dor
aab0b86646 Talk about projects using mxisd under the hood 2019-01-23 18:50:00 +01:00
Max Dor
3e22301af7 Properly handle /v1/store-invite 2019-01-16 02:57:40 +01:00
Max Dor
2b202323c0 Catch and handle more exceptions in Base HTTP handler 2019-01-16 02:57:40 +01:00
Max Dor
4ec05f518e Properly handle v1 of 3pid/bind 2019-01-16 02:57:40 +01:00
Max Dor
6da68298b0 Fix invalid paths 2019-01-16 02:57:40 +01:00
Max Dor
aecaafdeca Set theme jekyll for github pages 2019-01-16 01:31:21 +01:00
Max Dor
d885932f45 Fix loading failures of JDBC drivers for SQL-based Identity stores 2019-01-15 06:22:03 +01:00
Max Dor
c689a3f161 Fix classpath resources config 2019-01-13 00:30:52 +01:00
Max Dor
7805112548 Fix #110 2019-01-11 23:07:58 +01:00
Max Dor
3e89f0bc5e Fix #109 2019-01-11 23:07:52 +01:00
Max Dor
c6b8f7d48e Better handle of File reading / Input Streams 2019-01-11 23:02:57 +01:00
Max Dor
83377ebee0 Protect against NPE 2019-01-11 22:08:35 +01:00
Max Dor
2aa6e4d142 Fix missing .html from Spring to Undertow port 2019-01-11 22:08:22 +01:00
Max Dor
82a1a3df68 Fix invalid parsing of 3PID medium configs 2019-01-11 21:44:51 +01:00
Max Dor
7ec11ba8cf Use NetIQ config for NetIQ identity store instead of generic LDAP one 2019-01-07 04:32:12 +01:00
Max Dor
9317c11434 Use sane handler for all endpoints 2019-01-07 04:25:29 +01:00
Max Dor
b257a0275f Properly handle signing Key ID format 2019-01-07 04:19:53 +01:00
Max Dor
2aaa04062f Fix tests 2019-01-07 03:13:12 +01:00
Max Dor
54c3014568 Port distributions and start scripts to Undertow 2019-01-07 03:01:46 +01:00
Max Dor
c3ca73f576 Port documentation about Thymeleaf 2019-01-07 03:01:46 +01:00
Max Dor
4185b644b7 Continue structural port from Spring Boot to Undertow
- Configuration options
- Configuration documentation
2019-01-07 03:01:46 +01:00
Max Dor
ace5918342 Continue structural port from Spring Boot to Undertow
- Notification template generator
- Add tests for email notification handler
2019-01-07 03:01:46 +01:00
Max Dor
7ad985fead Continue structural port from Spring Boot to Undertow 2019-01-07 03:01:46 +01:00
Max Dor
6a376db322 Formatting (no-op) 2019-01-07 03:01:46 +01:00
Max Dor
950f7c931c Be consistent about testing package 2019-01-07 03:01:46 +01:00
Max Dor
d160a44509 Port default configuration values 2019-01-07 03:01:46 +01:00
Max Dor
05493da27c Start structural port from Spring Boot to Undertow 2019-01-07 03:01:46 +01:00
Max Dor
df44428a85 Fix #106 2019-01-04 19:26:45 +01:00
508 changed files with 22399 additions and 5702 deletions

3
.gitignore vendored
View File

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

View File

@@ -2,16 +2,17 @@ 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/mxisd VOLUME /etc/ma1sd
VOLUME /var/mxisd VOLUME /var/ma1sd
EXPOSE 8090 EXPOSE 8090
ENV JAVA_OPTS="" ENV JAVA_OPTS=""
ENV CONF_FILE_PATH="/etc/mxisd/mxisd.yaml" ENV CONF_FILE_PATH="/etc/ma1sd/ma1sd.yaml"
ENV SIGN_KEY_PATH="/var/mxisd/sign.key" ENV SIGN_KEY_PATH="/var/ma1sd/sign.key"
ENV SQLITE_DATABASE_PATH="/var/mxisd/mxisd.db" ENV SQLITE_DATABASE_PATH="/var/ma1sd/ma1sd.db"
CMD [ "/start.sh" ] CMD [ "/start.sh" ]
ADD src/docker/start.sh /start.sh ADD src/docker/start.sh /start.sh
ADD build/libs/mxisd.jar /mxisd.jar ADD src/script/ma1sd /app/ma1sd
ADD build/libs/ma1sd.jar /app/ma1sd.jar

View File

@@ -1,6 +1,6 @@
mxisd - Federated Matrix Identity Server ma1sd - Federated Matrix Identity Server
---------------------------------------- ----------------------------------------
![Travis-CI build status](https://travis-ci.org/kamax-matrix/mxisd.svg?branch=master) ![Travis-CI build status](https://travis-ci.org/ma1uta/ma1sd.svg?branch=master)
- [Overview](#overview) - [Overview](#overview)
- [Features](#features) - [Features](#features)
@@ -8,20 +8,29 @@ mxisd - Federated Matrix Identity Server
- [Getting Started](#getting-started) - [Getting Started](#getting-started)
- [Support](#support) - [Support](#support)
- [Contribute](#contribute) - [Contribute](#contribute)
- [Powered by ma1sd](#powered-by-ma1sd)
- [FAQ](#faq) - [FAQ](#faq)
- [Migration from mxisd](#migration-from-mxisd)
- [Contact](#contact) - [Contact](#contact)
---
* This project is a fork of the https://github.com/kamax-matrix/mxisd which has been archived and no longer supported. *
---
# Overview # Overview
mxisd is a Federated Matrix Identity server for self-hosted Matrix infrastructures with [enhanced features](#features). ma1sd is a Federated Matrix Identity server for self-hosted Matrix infrastructures with [enhanced features](#features).
As an enhanced Identity service, it implements the [Matrix Identity service API](https://kamax.io/matrix/api/identity_service/unstable.html) As an enhanced Identity service, it implements the [Identity service API](https://matrix.org/docs/spec/identity_service/r0.2.0.html)
and several [extra features](#features) that greatly enhance user experience within Matrix. and several [extra features](#features) that greatly enhance user experience within Matrix.
It is the one stop shop for anything regarding Authentication, Directory and Identity management in Matrix built in a It is the one stop shop for anything regarding Authentication, Directory and Identity management in Matrix built in a
single coherent product. single coherent product.
mxisd is specifically designed to connect to an existing on-premise Identity store (AD/Samba/LDAP, SQL Database, ma1sd is specifically designed to connect to an existing on-premise Identity store (AD/Samba/LDAP, SQL Database,
Web services/app, etc.) and ease the integration of a Matrix infrastructure within an existing one. Web services/app, etc.) and ease the integration of a Matrix infrastructure within an existing one.
Check [our FAQ entry](docs/faq.md#what-kind-of-setup-is-ma1sd-really-designed-for) to know if ma1sd is a good fit for you.
The core principle of mxisd is to map between Matrix IDs and 3PIDs (Third-Party IDentifiers) for the Homeserver and its The core principle of ma1sd is to map between Matrix IDs and 3PIDs (Third-Party IDentifiers) for the Homeserver and its
users. 3PIDs can be anything that uniquely and globally identify a user, like: users. 3PIDs can be anything that uniquely and globally identify a user, like:
- Email address - Email address
- Phone number - Phone number
@@ -32,15 +41,15 @@ users. 3PIDs can be anything that uniquely and globally identify a user, like:
If you are unfamiliar with the Identity vocabulary and concepts in Matrix, **please read this [introduction](docs/concepts.md)**. If you are unfamiliar with the Identity vocabulary and concepts in Matrix, **please read this [introduction](docs/concepts.md)**.
# Features # Features
[Identity](docs/features/identity.md): As a [regular Matrix Identity service](https://kamax.io/matrix/api/identity_service/unstable.html#general-principles): [Identity](docs/features/identity.md): As a [regular Matrix Identity service](https://matrix.org/docs/spec/identity_service/r0.2.0.html#general-principles):
- Search for people by 3PID using its own Identity stores - Search for people by 3PID using its own Identity stores
([Spec](https://kamax.io/matrix/api/identity_service/unstable.html#association-lookup)) ([Spec](https://matrix.org/docs/spec/identity_service/r0.2.0.html#association-lookup))
- Invite people to rooms by 3PID using its own Identity stores, with notifications to the invitee (Email, SMS, etc.) - Invite people to rooms by 3PID using its own Identity stores, with notifications to the invitee (Email, SMS, etc.)
([Spec](https://kamax.io/matrix/api/identity_service/unstable.html#post-matrix-identity-api-v1-store-invite)) ([Spec](https://matrix.org/docs/spec/identity_service/r0.2.0.html#invitation-storage))
- Allow users to add 3PIDs to their settings/profile - Allow users to add/remove 3PIDs to their settings/profile via 3PID sessions
([Spec](https://kamax.io/matrix/api/identity_service/unstable.html#establishing-associations)) ([Spec](https://matrix.org/docs/spec/identity_service/r0.2.0.html#establishing-associations))
- Register accounts on your Homeserver with 3PIDs - Register accounts on your Homeserver with 3PIDs
([Spec](https://kamax.io/matrix/api/identity_service/unstable.html#establishing-associations)) ([Spec](https://matrix.org/docs/spec/identity_service/r0.2.0.html#establishing-associations))
As an enhanced Identity service: As an enhanced Identity service:
- [Federation](docs/features/federation.md): Use a recursive lookup mechanism when searching and inviting people by 3PID, - [Federation](docs/features/federation.md): Use a recursive lookup mechanism when searching and inviting people by 3PID,
@@ -51,6 +60,7 @@ As an enhanced Identity service:
- Central Matrix Identity servers - Central Matrix Identity servers
- [Session Control](docs/threepids/session/session.md): Extensive control of where 3PIDs are transmitted so they are not - [Session Control](docs/threepids/session/session.md): Extensive control of where 3PIDs are transmitted so they are not
leaked publicly by users leaked publicly by users
- [Registration control](docs/features/registration.md): Control and restrict user registration based on 3PID patterns or criterias, like a pending invite
- [Authentication](docs/features/authentication.md): Use your Identity stores to perform authentication in [synapse](https://github.com/matrix-org/synapse) - [Authentication](docs/features/authentication.md): Use your Identity stores to perform authentication in [synapse](https://github.com/matrix-org/synapse)
via the [REST password provider](https://github.com/kamax-io/matrix-synapse-rest-auth) via the [REST password provider](https://github.com/kamax-io/matrix-synapse-rest-auth)
- [Directory search](docs/features/directory.md) which allows you to search for users within your organisation, - [Directory search](docs/features/directory.md) which allows you to search for users within your organisation,
@@ -66,40 +76,43 @@ As an enhanced Identity service:
- Users can directly find each other using whatever attribute is relevant within your Identity store - Users can directly find each other using whatever attribute is relevant within your Identity store
- Federate your Identity server so you can discover others and/or others can discover you - Federate your Identity server so you can discover others and/or others can discover you
Also, check [our FAQ entry](docs/faq.md#what-kind-of-setup-is-ma1sd-really-designed-for) to know if ma1sd is a good fit for you.
# Getting started # Getting started
See the [dedicated document](docs/getting-started.md) See the [dedicated document](docs/getting-started.md)
# Support # Support
## Community ## Troubleshooting
Over Matrix: [#mxisd:kamax.io](https://matrix.to/#/#mxisd:kamax.io) ([Preview](https://view.matrix.org/room/!NPRUEisLjcaMtHIzDr:kamax.io/)) A basic troubleshooting guide is available [here](docs/troubleshooting.md).
For more high-level discussion about the Identity Server architecture/API, go to [#matrix-identity:kamax.io](https://matrix.to/#/#matrix-identity:kamax.io) ## Community
Over Matrix: [#ma1sd:ru-matrix.org](https://matrix.to/#/#ma1sd:ru-matrix.org) ([Preview](https://view.matrix.org/room/!CxwBdgAlaphCARnKTA:ru-matrix.org/))
## Commercial ## Commercial
If you would prefer professional support/custom development for mxisd and/or for Matrix in general, including other open Sorry, I cannot provide commercial support (at least now). But always try to help you.
source technologies/products:
- Visit our [website](https://www.kamax.io/) to get in touch with us and get a quote. Don't hesitate to ask about project and feel free to create issues at https://github.com/ma1uta/ma1sd
- Come in our general Matrix room: [#kamax-matrix:kamax.io](https://matrix.to/#/#kamax-matrix:kamax.io)
# Contribute # Contribute
You can contribute as a community member by: You can contribute as a community member by:
- Giving us feedback about your usage of mxisd, even if it seems unimportant or if all is working well! - Giving us feedback about your usage of ma1sd, even if it seems unimportant or if all is working well!
- Opening issues for any weird behaviour or bug. mxisd should feel natural, let us know if it does not! - Opening issues for any weird behaviour or bug. ma1sd should feel natural, let us know if it does not!
- Helping us improve the documentation: tell us what is good or not good (in an issue or in Matrix), or make a PR with - Helping us improve the documentation: tell us what is good or not good (in an issue or in Matrix), or make a PR with
changes you feel improve the doc. changes you feel improve the doc.
- Contribute code directly: we love contributors! All your contributions will be licensed under AGPLv3. - Contribute code directly: we love contributors! All your contributions will be licensed under AGPLv3.
- [Donate!](https://liberapay.com/maximusdor/) Any donation is welcome, regardless how small or big, and will directly
be used for the fixed costs and developer time of mxisd.
You can contribute as an organisation/corporation by: # Powered by ma1sd
- Get a [support contract](#commercial). This is the best way you can help us as it ensures mxisd is The following projects can use ma1sd under the hood for some or all their features. Check them out!
maintained regularly and you get direct access to the support team. - [matrix-docker-ansible-deploy](https://github.com/spantaleev/matrix-docker-ansible-deploy)
- Sponsoring new features or bug fixes. [Get in touch](#contact) so we can discuss it further. - [matrix-register-bot](https://github.com/krombel/matrix-register-bot)
# FAQ # FAQ
See the [dedicated document](docs/faq.md) See the [dedicated document](docs/faq.md)
# Migration from mxisd
See the [migration guide](docs/migration-from-mxisd.md)
# Contact # Contact
Get in touch via: Get in touch via:
- Matrix: [#mxisd:kamax.io](https://matrix.to/#/#mxisd:kamax.io) - Matrix: [#ma1sd:ru-matrix.org](https://matrix.to/#/#ma1sd:ru-matrix.org)
- Email: see our website: [Kamax.io](https://www.kamax.io)

View File

@@ -1,114 +0,0 @@
# Sample configuration file explaining the minimum required keys to be set to run mxisd
#
# For a complete list of options, see https://github.com/kamax-matrix/mxisd
#######################
# Matrix config items #
#######################
# Matrix domain, same as the domain configure in your Homeserver configuration.
# (note: in Synapse Homeserver, the Matrix domain may be defined as 'server_name' in configuration file).
#
# This is used to build the various identifiers for identity, auth and directory.
matrix.domain: ''
################
# Signing keys #
################
# Absolute path for the Identity Server signing key.
# During testing, /var/tmp/mxisd.key is a possible value
#
# For production, recommended location shall be one of the following:
# - /var/opt/mxisd/sign.key
# - /var/local/mxisd/sign.key
# - /var/lib/mxisd/sign.key
#
# The signing key is auto-generated during execution time if not present.
key.path: ''
############################
# Persistence config items #
############################
# Configure the storage backend, usually a DB
# Possible built-in values:
# sqlite SQLite backend, default
#
#storage.backend: 'sqlite'
# Path to the SQLite DB file
#
# Examples:
# - /var/opt/mxisd/mxisd.db
# - /var/local/mxisd/mxisd.db
# - /var/lib/mxisd/mxisd.db
#
storage.provider.sqlite.database: '/path/to/mxisd.db'
####################
# Fallback servers #
####################
#
# Root/Central servers to be used as final fallback when performing lookups.
# By default, for privacy reasons, matrix.org servers are not enabled anymore.
# See the following issue: https://github.com/kamax-matrix/mxisd/issues/76
#
# If you would like to use them and trade away your privacy for convenience, uncomment the following option:
#
#forward.servers: ['matrix-org']
################
# LDAP Backend #
################
# If you would like to integrate with your AD/Samba/LDAP server,
# see https://github.com/kamax-matrix/mxisd/blob/master/docs/stores/ldap.md
###############
# SQL Backend #
###############
# If you would like to integrate with a MySQL/MariaDB/PostgreQL/SQLite DB,
# see https://github.com/kamax-matrix/mxisd/blob/master/docs/stores/sql.md
################
# REST Backend #
################
# If you would like to integrate with an existing web service/webapp,
# see https://github.com/kamax-matrix/mxisd/blob/master/docs/stores/rest.md
#################################################
# Notifications for invites/addition to profile #
#################################################
# If you would like to change the content,
# see https://github.com/kamax-matrix/mxisd/blob/master/docs/threepids/notification/template-generator.md
#
#### E-mail invite sender
#
# SMTP host
threepid.medium.email.connectors.smtp.host: "smtp.example.org"
# SMTP port
threepid.medium.email.connectors.smtp.port: 587
# TLS mode for the connection.
#
# Possible values:
# 0 Disable TLS entirely
# 1 Enable TLS if supported by server (default)
# 2 Force TLS and fail if not available
#
#threepid.medium.email.connectors.smtp.tls: 1
# Login for SMTP
threepid.medium.email.connectors.smtp.login: "matrix-identity@example.org"
# Password for the account
threepid.medium.email.connectors.smtp.password: "ThePassword"
# The e-mail to send as.
threepid.medium.email.identity.from: "matrix-identity@example.org"

View File

@@ -1,5 +1,5 @@
/* /*
* mxisd - Matrix Identity Server Daemon * ma1sd - Matrix Identity Server Daemon
* Copyright (C) 2017 Kamax Sarl * Copyright (C) 2017 Kamax Sarl
* *
* https://www.kamax.io/ * https://www.kamax.io/
@@ -21,17 +21,21 @@
import java.util.regex.Pattern import java.util.regex.Pattern
apply plugin: 'java' apply plugin: 'java'
apply plugin: 'org.springframework.boot' apply plugin: 'application'
apply plugin: 'com.github.johnrengelman.shadow'
apply plugin: 'idea'
apply plugin: 'com.github.ben-manes.versions'
def confFileName = "application.example.yaml" def confFileName = "ma1sd.example.yaml"
def distDir = "${project.buildDir}/dist" def distDir = "${project.buildDir}/dist"
def debBinPath = "/usr/lib/mxisd" def debBinPath = "/usr/lib/ma1sd"
def debConfPath = "/etc/mxisd" def debConfPath = "/etc/ma1sd"
def debDataPath = "/var/lib/mxisd" def debDataPath = "/var/lib/ma1sd"
def debSystemdPath = "/etc/systemd/system" def debSystemdPath = "/etc/systemd/system"
def debConfFileName = "mxisd-sample.yaml" def debConfFileName = confFileName
def debStartScriptFilename = "ma1sd"
def debBuildBasePath = "${project.buildDir}/tmp/debian" def debBuildBasePath = "${project.buildDir}/tmp/debian"
def debBuildDebianPath = "${debBuildBasePath}/DEBIAN" def debBuildDebianPath = "${debBuildBasePath}/DEBIAN"
@@ -40,13 +44,18 @@ def debBuildConfPath = "${debBuildBasePath}${debConfPath}"
def debBuildDataPath = "${debBuildBasePath}${debDataPath}" def debBuildDataPath = "${debBuildBasePath}${debDataPath}"
def debBuildSystemdPath = "${debBuildBasePath}${debSystemdPath}" def debBuildSystemdPath = "${debBuildBasePath}${debSystemdPath}"
def dockerImageName = "kamax/mxisd" def dockerImageName = "ma1uta/ma1sd"
def dockerImageTag = "${dockerImageName}:${mxisdVersion()}" def dockerImageTag = "${dockerImageName}:${ma1sdVersion()}"
String mxisdVersion() { group = 'io.kamax'
mainClassName = 'io.kamax.mxisd.MxisdStandaloneExec'
sourceCompatibility = '1.8'
targetCompatibility = '1.8'
String ma1sdVersion() {
def versionPattern = Pattern.compile("v(\\d+\\.)?(\\d+\\.)?(\\d+)(-.*)?") def versionPattern = Pattern.compile("v(\\d+\\.)?(\\d+\\.)?(\\d+)(-.*)?")
String version = System.getenv('MXISD_BUILD_VERSION') String version = System.getenv('MA1SD_BUILD_VERSION')
if (version == null || version.size() == 0) { if (version == null || version.size() == 0) {
version = gitVersion() version = gitVersion()
} }
@@ -59,129 +68,132 @@ String gitVersion() {
commandLine = ['git', 'describe', '--tags', '--always', '--dirty'] commandLine = ['git', 'describe', '--tags', '--always', '--dirty']
standardOutput = out standardOutput = out
} }
return out.toString().replace(System.lineSeparator(), ''); return out.toString().replace(System.lineSeparator(), '')
} }
buildscript { buildscript {
repositories { repositories {
mavenCentral() jcenter()
} }
dependencies { dependencies {
classpath 'org.springframework.boot:spring-boot-gradle-plugin:1.5.3.RELEASE' classpath 'com.github.jengelman.gradle.plugins:shadow:5.1.0'
classpath 'com.github.ben-manes:gradle-versions-plugin:0.21.0'
} }
} }
repositories { repositories {
mavenCentral() jcenter()
maven { url "https://kamax.io/maven/releases/" }
maven { url "https://kamax.io/maven/snapshots/" }
} }
dependencies { dependencies {
// Logging
compile 'org.slf4j:slf4j-simple:1.7.25'
// Easy file management // Easy file management
compile 'commons-io:commons-io:2.5' compile 'commons-io:commons-io:2.6'
// Spring Boot - standalone app // Config management
compile 'org.springframework.boot:spring-boot-starter-web:1.5.10.RELEASE' compile 'org.yaml:snakeyaml:1.24'
// Thymeleaf for HTML templates // Dependencies from old Matrix-java-sdk
compile "org.springframework.boot:spring-boot-starter-thymeleaf:1.5.10.RELEASE" compile 'org.apache.commons:commons-lang3:3.9'
compile 'com.squareup.okhttp3:okhttp:4.0.1'
compile 'commons-codec:commons-codec:1.12'
// Matrix Java SDK // ORMLite
compile 'io.kamax:matrix-java-sdk:0.0.14-8-g0e57ec6' compile 'com.j256.ormlite:ormlite-jdbc:5.1'
// ed25519 handling // ed25519 handling
compile 'net.i2p.crypto:eddsa:0.1.0' compile 'net.i2p.crypto:eddsa:0.3.0'
// LDAP connector // LDAP connector
compile 'org.apache.directory.api:api-all:1.0.0' compile 'org.apache.directory.api:api-all:1.0.0'
// DNS lookups // DNS lookups
compile 'dnsjava:dnsjava:2.1.8' compile 'dnsjava:dnsjava:2.1.9'
// HTTP connections // HTTP connections
compile 'org.apache.httpcomponents:httpclient:4.5.3' compile 'org.apache.httpcomponents:httpclient:4.5.9'
// Phone numbers validation // Phone numbers validation
compile 'com.googlecode.libphonenumber:libphonenumber:8.7.1' compile 'com.googlecode.libphonenumber:libphonenumber:8.10.15'
// E-mail sending // E-mail sending
compile 'com.sun.mail:javax.mail:1.6.2'
compile 'javax.mail:javax.mail-api:1.6.2' compile 'javax.mail:javax.mail-api:1.6.2'
compile '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' compile 'com.google.firebase:firebase-admin:5.3.0'
// ORMLite
compile 'com.j256.ormlite:ormlite-jdbc:5.0'
// Connection Pool // Connection Pool
compile 'com.mchange:c3p0:0.9.5.2' compile 'com.mchange:c3p0:0.9.5.4'
// SQLite // SQLite
compile 'org.xerial:sqlite-jdbc:3.20.0' compile 'org.xerial:sqlite-jdbc:3.28.0'
// PostgreSQL // PostgreSQL
compile 'org.postgresql:postgresql:42.1.4' compile 'org.postgresql:postgresql:42.2.6'
// MariaDB/MySQL // MariaDB/MySQL
compile 'org.mariadb.jdbc:mariadb-java-client:2.1.2' compile 'org.mariadb.jdbc:mariadb-java-client:2.4.2'
// Twilio SDK for SMS // Twilio SDK for SMS
compile 'com.twilio.sdk:twilio:7.14.5' compile 'com.twilio.sdk:twilio:7.40.1'
// SendGrid SDK to send emails from GCE // SendGrid SDK to send emails from GCE
compile 'com.sendgrid:sendgrid-java:2.2.2' compile 'com.sendgrid:sendgrid-java:2.2.2'
// ZT-Exec for exec identity store // ZT-Exec for exec identity store
compile 'org.zeroturnaround:zt-exec:1.10' compile 'org.zeroturnaround:zt-exec:1.11'
testCompile 'junit:junit:4.12' // HTTP server
testCompile 'com.github.tomakehurst:wiremock:2.8.0' compile 'io.undertow:undertow-core:2.0.22.Final'
testCompile 'com.unboundid:unboundid-ldapsdk:4.0.9'
// Command parser for AS interface
implementation 'commons-cli:commons-cli:1.4'
testCompile 'junit:junit:4.13-beta-3'
testCompile 'com.github.tomakehurst:wiremock:2.24.0'
testCompile 'com.unboundid:unboundid-ldapsdk:4.0.11'
testCompile 'com.icegreen:greenmail:1.5.10'
} }
springBoot { jar {
executable = true manifest {
attributes(
embeddedLaunchScriptProperties = [ 'Implementation-Version': ma1sdVersion()
confFolder: "/etc/default" )
] }
} }
processResources { shadowJar {
baseName = project.name
classifier = null
version = null
}
task debBuild(dependsOn: shadowJar) {
doLast { doLast {
copy { String debVersion = ma1sdVersion()
from('build/resources/main/application.yaml') { println "Version for package: ${debVersion}"
rename 'application.yaml', 'mxisd.yaml'
}
into 'build/resources/main'
}
}
}
task buildDeb(dependsOn: build) {
doLast {
def v = mxisdVersion()
println "Version for package: ${v}"
mkdir distDir mkdir distDir
mkdir debBuildBasePath mkdir debBuildBasePath
mkdir "${debBuildBasePath}/DEBIAN" mkdir debBuildDebianPath
mkdir debBuildBinPath mkdir debBuildBinPath
mkdir debBuildConfPath mkdir debBuildConfPath
mkdir debBuildDataPath mkdir debBuildDataPath
mkdir debBuildSystemdPath mkdir debBuildSystemdPath
copy { copy {
from "${project.buildDir}/libs/mxisd.jar" from "${project.buildDir}/libs/ma1sd.jar"
into debBuildBinPath into debBuildBinPath
} }
ant.chmod( copy {
file: "${debBuildBinPath}/mxisd.jar", from "${project.file("src/script/" + debStartScriptFilename)}"
perm: 'a+x' into debBuildBinPath
) }
copy { copy {
from(project.file(confFileName)) { from(project.file(confFileName)) {
@@ -190,16 +202,16 @@ task buildDeb(dependsOn: build) {
into debBuildConfPath into debBuildConfPath
} }
ant.replaceregexp( ant.replaceregexp( // FIXME adapt to new config format
file: "${debBuildConfPath}/${debConfFileName}", file: "${debBuildConfPath}/${debConfFileName}",
match: "key.path:(.*)", match: "key:\\R path:(.*)",
replace: "key.path: '${debDataPath}/signing.key'" replace: "key:\n path: '${debDataPath}/keys'"
) )
ant.replaceregexp( ant.replaceregexp( // FIXME adapt to new config format
file: "${debBuildConfPath}/${debConfFileName}", file: "${debBuildConfPath}/${debConfFileName}",
match: "storage.provider.sqlite.database:(.*)", match: "storage:\\R provider:\\R sqlite:\\R database:(.*)",
replace: "storage.provider.sqlite.database: '${debDataPath}/mxisd.db'" replace: "storage:\n provider:\n sqlite:\n database: '${debDataPath}/store.db'"
) )
copy { copy {
@@ -210,7 +222,7 @@ task buildDeb(dependsOn: build) {
ant.replace( ant.replace(
file: "${debBuildDebianPath}/control", file: "${debBuildDebianPath}/control",
token: 'Version: 0', token: 'Version: 0',
value: "Version: ${v}" value: "Version: ${debVersion}"
) )
ant.replace( ant.replace(
@@ -219,6 +231,12 @@ task buildDeb(dependsOn: build) {
value: debDataPath value: debDataPath
) )
ant.replace(
file: "${debBuildDebianPath}/postinst",
token: '%DEB_CONF_FILE%',
value: "${debConfPath}/ma1sd.yaml"
)
ant.chmod( ant.chmod(
file: "${debBuildDebianPath}/postinst", file: "${debBuildDebianPath}/postinst",
perm: 'a+x' perm: 'a+x'
@@ -230,7 +248,7 @@ task buildDeb(dependsOn: build) {
) )
copy { copy {
from "${project.file('src/systemd/mxisd.service')}" from "${project.file('src/systemd/ma1sd.service')}"
into debBuildSystemdPath into debBuildSystemdPath
} }
@@ -246,7 +264,7 @@ task buildDeb(dependsOn: build) {
} }
} }
task dockerBuild(type: Exec, dependsOn: build) { task dockerBuild(type: Exec, dependsOn: shadowJar) {
commandLine 'docker', 'build', '-t', dockerImageTag, project.rootDir commandLine 'docker', 'build', '-t', dockerImageTag, project.rootDir
doLast { doLast {

View File

@@ -5,6 +5,7 @@
- Installation - Installation
- [Debian package](install/debian.md) - [Debian package](install/debian.md)
- [ArchLinux](install/archlinux.md) - [ArchLinux](install/archlinux.md)
- [NixOS](install/nixos.md)
- [Docker](install/docker.md) - [Docker](install/docker.md)
- [From source](install/source.md) - [From source](install/source.md)
- [Architecture overview](architecture.md) - [Architecture overview](architecture.md)

View File

@@ -1 +1 @@
theme: jekyll-theme-cayman theme: jekyll-theme-hacker

View File

@@ -16,7 +16,7 @@ TCP 443
+<---------------------------------<+ +<---------------------------------<+
| |
| +-------------------+ | +-------------------+
TCP 8090 +-> | mxisd | TCP 8090 +-> | ma1sd |
| | | |
| - Profile's 3PIDs | | - Profile's 3PIDs |
| - 3PID Invites | | - 3PID Invites |
@@ -25,7 +25,7 @@ TCP 443
TCP 443 TCP 443
| +------------------------+ | +------------------------+
| | Remote Federated | | | Remote Federated |
| | mxisd servers | | | ma1sd servers |
| | | | | |
+--> - 3PID Invites | +--> - 3PID Invites |
+------------------------+ +------------------------+

View File

@@ -12,30 +12,31 @@
### Build ### Build
```bash ```bash
git clone https://github.com/kamax-matrix/mxisd.git git clone https://github.com/ma1uta/ma1sd.git
cd mxisd cd ma1sd
./gradlew build ./gradlew build
``` ```
Create a new configuration file by coping `application.example.yaml` to `application.yaml` and edit to your needs. Create a new configuration file by coping `ma1sd.example.yaml` to `ma1sd.yaml` and edit to your needs.
For advanced configuration, see the [Configure section](configure.md). For advanced configuration, see the [Configure section](configure.md).
**NOTE**: `application.yaml` is also called `mxisd.yaml` in some specific installations.
Start the server in foreground to validate the build and configuration: Start the server in foreground to validate the build and configuration:
```bash ```bash
java -jar build/libs/mxisd.jar java -jar build/libs/ma1sd.jar
``` ```
Ensure the signing key is available: Ensure the signing key is available:
```bash ```bash
$ curl 'http://localhost:8090/_matrix/identity/api/v1/pubkey/ed25519:0' $ curl 'http://localhost:8090/_matrix/identity/api/v1/pubkey/ed25519:0'
{"public_key":"..."} {"public_key":"..."}
``` ```
Test basic recursive lookup (requires Internet connection with access to TCP 443): Test basic recursive lookup (requires Internet connection with access to TCP 443):
```bash ```bash
$ curl 'http://localhost:8090/_matrix/identity/api/v1/lookup?medium=email&address=mxisd-federation-test@kamax.io' $ curl 'http://localhost:8090/_matrix/identity/api/v1/lookup?medium=email&address=ma1sd-federation-test@kamax.io'
{"address":"mxisd-federation-test@kamax.io","medium":"email","mxid":"@mxisd-lookup-test:kamax.io",...}
{"address":"ma1sd-federation-test@kamax.io","medium":"email","mxid":"@ma1sd-lookup-test:kamax.io",...}
``` ```
If you enabled LDAP, you can also validate your config with a similar request after replacing the `address` value with If you enabled LDAP, you can also validate your config with a similar request after replacing the `address` value with
@@ -55,15 +56,15 @@ Requirements:
- fakeroot - fakeroot
- dpkg-deb - dpkg-deb
[Build mxisd](#build) then: [Build ma1sd](#build) then:
```bash ```bash
./gradlew buildDeb ./gradlew debBuild
``` ```
You will find the debian package in `build/dist`. You will find the debian package in `build/dist`.
Then follow the instruction in the [Debian package](install/debian.md) document. Then follow the instruction in the [Debian package](install/debian.md) document.
## Docker image ## Docker image
[Build mxisd](#build) then: [Build ma1sd](#build) then:
```bash ```bash
./gradlew dockerBuild ./gradlew dockerBuild
``` ```

View File

@@ -1,6 +1,6 @@
# Concepts # Concepts
- [Matrix](#matrix) - [Matrix](#matrix)
- [mxisd](#mxisd) - [ma1sd](#ma1sd)
## Matrix ## Matrix
The following concepts are part of the Matrix ecosystem and specification. The following concepts are part of the Matrix ecosystem and specification.
@@ -15,7 +15,7 @@ A 3PID is a globally unique canonical identifier which is made of:
- Medium, which describes what network it belongs to (Email, Phone, Twitter, Discord, etc.) - Medium, which describes what network it belongs to (Email, Phone, Twitter, Discord, etc.)
- Address, the actual value people typically use on a daily basis. - Address, the actual value people typically use on a daily basis.
mxisd core mission is to map those identifiers to Matrix User IDs. ma1sd core mission is to map those identifiers to Matrix User IDs.
### Homeserver ### Homeserver
Where a user **account and data** are stored. Where a user **account and data** are stored.
@@ -34,10 +34,10 @@ An Identity server:
### 3PID session ### 3PID session
The fact to validate a 3PID (email, phone number, etc.) via the introduction of a token which was sent to the 3PID address. The fact to validate a 3PID (email, phone number, etc.) via the introduction of a token which was sent to the 3PID address.
## mxisd ## ma1sd
The following concepts are specific to mxisd. The following concepts are specific to ma1sd.
### Identity store ### Identity store
Where your user accounts and 3PID mappings are stored. Where your user accounts and 3PID mappings are stored.
mxisd itself **DOES NOT STORE** user accounts or 3PID mappings. ma1sd itself **DOES NOT STORE** user accounts or 3PID mappings.

View File

@@ -1,7 +1,6 @@
# Configuration # Configuration
- [Concepts](#concepts) - [Concepts](#concepts)
- [Syntax](#syntax) - [Syntax](#syntax)
- [Variables](#variables)
- [Matrix](#matrix) - [Matrix](#matrix)
- [Server](#server) - [Server](#server)
- [Storage](#storage) - [Storage](#storage)
@@ -11,36 +10,15 @@
## Concepts ## Concepts
### Syntax ### Syntax
The configuration file is [YAML](http://yaml.org/) based, allowing two types of syntax. The configuration file is [YAML](http://yaml.org/) based:
Properties-like:
```yaml
my.config.item: 'value'
```
Object-like:
```yaml ```yaml
my: my:
config: config:
item: 'value' item: 'value'
``` ```
These can also be combined within the same file.
Both syntax will be used interchangeably in these documents.
### Variables
It is possible to copy the value of a configuration item into another using the syntax `${config.key.item}`.
Example that will copy the value of `matrix.domain` into `server.name`:
```yaml
matrix:
domain: 'example.org'
server:
name: '${matrix.domain}'
```
**WARNING:** mxisd might overwrite/adapt some values during launch. Those changes will not be reflected into copied keys.
When referencing keys in all documents, a property-like shorthand will be used. The shorthand for the above example would be `my.config.item`
## Matrix ## Matrix
`matrix.domain` `matrix.domain`
@@ -53,17 +31,27 @@ Namespace to create arbitrary list of Identity servers, usable in other parts of
Example: Example:
```yaml ```yaml
matrix.identity.servers: matrix:
identity:
servers:
myOtherServers: myOtherServers:
- 'https://other1.example.org' - 'https://other1.example.org'
- 'https://other2.example.org' - 'https://other2.example.org'
``` ```
Create a list under the label `root` containing a single Identity server, `https://matrix.org` Create a list under the label `myOtherServers` containing two Identity servers: `https://other1.example.org` and `https://other2.example.org`.
## Server ## Server
- `server.name`: Public hostname of mxisd, if different from the Matrix domain. - `server.name`: Public hostname of ma1sd, if different from the Matrix domain.
- `server.port`: HTTP port to listen on (unencrypted) - `server.port`: HTTP port to listen on (unencrypted)
- `server.publicUrl`: Defaults to `https://${server.name}` - `server.publicUrl`: Defaults to `https://{server.name}`
## Unbind (MSC1915)
- `session.policy.unbind.enabled`: Enable or disable unbind functionality (MSC1915). (Defaults to true).
*Warning*: Unbind check incoming request by two ways:
- session validation.
- request signature via `X-Matrix` header and uses `server.publicUrl` property to construct the signing json;
Commonly the `server.publicUrl` should be the same value as the `trusted_third_party_id_servers` property in the synapse config.
## Storage ## Storage
### SQLite ### SQLite
@@ -82,19 +70,23 @@ See the dedicated documents:
Example: Example:
```yaml ```yaml
notification.handler.email: 'sendgrid' notification:
notification.handler.msisdn: 'raw' handler:
email: 'sendgrid'
msisdn: 'raw'
``` ```
- Emails notifications would use the `sendgrid` handler, which define its own configuration under `notification.handlers.sendgrid` - Emails notifications would use the `sendgrid` handler, which define its own configuration under `notification.handlers.sendgrid`
- Phone notification would use the `raw` handler, basic default built-in handler of mxisd - Phone notification would use the `raw` handler, basic default built-in handler in ma1sd
### Handlers ### Handlers
- `notification.handers.<handler ID>`: Handler-specific configuration for the given handler ID. Repeatable. - `notification.handers.<handler ID>`: Handler-specific configuration for the given handler ID. Repeatable.
Example: Example:
```yaml ```yaml
notification.handlers.raw: ... notification:
notification.handlers.sendgrid: ... handlers:
raw: ...
sendgrid: ...
``` ```
Built-in: Built-in:

View File

@@ -10,23 +10,32 @@ first basic setup running which relies on you reading the documentation in the r
- [Identity stores](stores/README.md) you wish to fetch data from. - [Identity stores](stores/README.md) you wish to fetch data from.
- [Features](features) you are interested in that will use your Identity store(s) data. - [Features](features) you are interested in that will use your Identity store(s) data.
**IMPORTANT**: Be aware that mxisd tries to fit within the current protocol and existing products and basic understanding **IMPORTANT**: Be aware that ma1sd tries to fit within the current protocol and existing products and basic understanding
of the Matrix protocol is required for some advanced features. of the Matrix protocol is required for some advanced features.
If all fails, come over to [the project room](https://matrix.to/#/#mxisd:kamax.io) and we'll do our best to get you If all fails, come over to [the project room](https://matrix.to/#/#ma1sd:ru-matrix.org) and we'll do our best to get you
started and answer questions you might have. started and answer questions you might have.
### Do I need to use mxisd if I run a Homeserver? ### What kind of setup is ma1sd really designed for?
ma1sd is primarily designed for setups that:
- [Care for their privacy](https://github.com/kamax-matrix/ma1sd/wiki/ma1sd-and-your-privacy)
- Have their own [domains](https://en.wikipedia.org/wiki/Domain_name)
- Use those domains for their email addresses and all other services
- Already have an [Identity store](stores/README.md), typically [LDAP-based](stores/ldap.md).
If you meet all the conditions, then you are the prime use case we designed ma1sd for.
If you meet some of the conditions, but not all, ma1sd will still be a good fit for you but you won't fully enjoy all its
features.
### Do I need to use ma1sd if I run a Homeserver?
No, but it is strongly recommended, even if you don't use any Identity store or integration. No, but it is strongly recommended, even if you don't use any Identity store or integration.
In its default configuration, mxisd uses other federated public servers when performing queries. In its default configuration, ma1sd uses other federated public servers when performing queries.
It can also [be configured](features/identity.md#lookups) to use the central matrix.org servers, giving you access to at It can also [be configured](features/identity.md#lookups) to use the central matrix.org servers, giving you access to at
least the same information as if you were not running it. least the same information as if you were not running it.
It will also give your users a choice to make their 3PIDs available publicly, ensuring they are made aware of the So ma1sd is like your gatekeeper and guardian angel. It does not change what you already know, just adds some nice
privacy consequences, which is not the case with the central Matrix.org servers.
So mxisd is like your gatekeeper and guardian angel. It does not change what you already know, just adds some nice
simple features on top of it. simple features on top of it.
### I'm not sure I understand what an "Identity server" is supposed to be or do... ### I'm not sure I understand what an "Identity server" is supposed to be or do...
@@ -35,27 +44,28 @@ what they want to do with that part of the ecosystem. Therefore, "Identity" is c
Given the scope of the current Identity Service API, it would be best called "Invitation service". Given the scope of the current Identity Service API, it would be best called "Invitation service".
Because the current scope is so limited and no integration is done with the Homeserver, there was a big lack of features Because the current scope is so limited and no integration is done with the Homeserver, there was a big lack of features
for groups/corporations/organisation. This is where mxisd comes in. for groups/corporations/organisation. This is where ma1sd comes in.
mxisd implements the Identity Service API and also a set of features which are expected by regular users, truly living ma1sd implements the Identity Service API and also a set of features which are expected by regular users, truly living
up to its "Identity server" name. up to its "Identity server" name.
### Can I migrate my existing account on another Matrix server with mxisd? ### Can I migrate my existing account on another Matrix server with ma1sd?
No. No.
Accounts cannot currently migrate/move from one server to another. Accounts cannot currently migrate/move from one server to another.
See a [brief explanation document](concepts.md) about Matrix and mxisd 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 mxisd? ### 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 and The [synapse LDAP3 auth provider](https://github.com/matrix-org/matrix-synapse-ldap3) is not longer maintained despite
only handles on 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
- Integrate with Identity management - Integrate with Identity management
- Integrate with Directory searches - Integrate with Directory searches
- Integrate with Profile data
mxisd is a replacement and enhancement of it, offering coherent results in all areas, which the LDAP3 auth provider ma1sd is a replacement and enhancement of it, offering coherent results in all areas, which the LDAP3 auth provider
does not. does not.
### Sydent is the official Identity server implementation of the Matrix team. Why not use that? ### Sydent is the official Identity server implementation of the Matrix team. Why not use that?
@@ -66,22 +76,22 @@ You can, but [sydent](https://github.com/matrix-org/sydent):
- forces you to duplicate all your identity data, so people can be found by 3PIDs - forces you to duplicate all your identity data, so people can be found by 3PIDs
- forces users to enter all their emails and phone numbers manually in their profile - forces users to enter all their emails and phone numbers manually in their profile
So really, you should go with mxisd. So really, you should go with ma1sd.
### Will I loose access to the central Matrix.org/Vector.im Identity data if I use mxisd? ### Will I loose access to the central Matrix.org/Vector.im Identity data if I use ma1sd?
No. No.
In its default configuration, mxisd does not talk to the central Identity server matrix.org to avoid leaking your private In its default configuration, ma1sd does not talk to the central Identity server matrix.org to avoid leaking your private
data and those of people you might know. data and those of people you might know.
mxisd [can be configured](features/identity.md#lookups) to talk to the central Identity servers if you wish. [You can configure it](features/identity.md#lookups) to talk to the central Identity servers if you wish.
### So mxisd is just a big hack! I don't want to use non-official features! ### So ma1sd is just a big hack! I don't want to use non-official features!
mxisd primary concerns are your privacy and to always be compatible with the Matrix ecosystem and the Identity service API. ma1sd primary concerns are your privacy and to always be compatible with the Matrix ecosystem and the Identity service API.
Whenever the API will be updated and/or enhanced, mxisd will follow, remaining 100% compatible with the ecosystem. Whenever the API will be updated and/or enhanced, ma1sd will follow, remaining 100% compatible with the ecosystem.
### Should I use mxisd if I don't host my own Homeserver? ### Should I use ma1sd if I don't host my own Homeserver?
No. No.
It is possible, but it is not supported and the scope of features will be extremely limited. It is possible, but it is not supported and the scope of features will be extremely limited.
Please consider hosting your own Homeserver and using mxisd alongside it. Please consider hosting your own Homeserver and using ma1sd alongside it.

View File

@@ -3,7 +3,7 @@
- [Basic](#basic) - [Basic](#basic)
- [Overview](#overview) - [Overview](#overview)
- [synapse](#synapse) - [synapse](#synapse)
- [mxisd](#mxisd) - [ma1sd](#ma1sd)
- [Validate](#validate) - [Validate](#validate)
- [Next steps](#next-steps) - [Next steps](#next-steps)
- [Profile auto-fil](#profile-auto-fill) - [Profile auto-fil](#profile-auto-fill)
@@ -16,15 +16,15 @@
- [DNS Overwrite](#dns-overwrite) - [DNS Overwrite](#dns-overwrite)
## Description ## Description
Authentication is an enhanced feature of mxisd to ensure coherent and centralized identity management. Authentication is an enhanced feature of ma1sd to ensure coherent and centralized identity management.
It allows to use Identity stores configured in mxisd to authenticate users on your Homeserver. It allows to use Identity stores configured in ma1sd to authenticate users on your Homeserver.
Authentication is divided into two parts: Authentication is divided into two parts:
- [Basic](#basic): authenticate with a regular username. - [Basic](#basic): authenticate with a regular username.
- [Advanced](#advanced): same as basic with extra ability to authenticate using a 3PID. - [Advanced](#advanced): same as basic with extra abilities like authenticate using a 3PID or do username rewrite.
## Basic ## Basic
Authentication by username is possible by linking synapse and mxisd together using a specific module for synapse, also Authentication by username is possible by linking synapse and ma1sd together using a specific module for synapse, also
known as password provider. known as password provider.
### Overview ### Overview
@@ -33,7 +33,7 @@ An overview of the Basic Authentication process:
Identity stores Identity stores
Client +------+ Client +------+
| +-------------------------+ +--> | LDAP | | +-------------------------+ +--> | LDAP |
| +---------------+ /_matrix/identity | mxisd | | +------+ | +---------------+ /_matrix/identity | ma1sd | | +------+
+-> | Reverse proxy | >------------------+ | | | +-> | Reverse proxy | >------------------+ | | |
+--|------------+ | | | | +--------+ +--|------------+ | | | | +--------+
| +-----> Check ID stores >------+--> | SQL DB | | +-----> Check ID stores >------+--> | SQL DB |
@@ -49,20 +49,20 @@ An overview of the Basic Authentication process:
| user profiles | If valid credentials and supported by Identity store(s) | user profiles | If valid credentials and supported by Identity store(s)
+--------------------------+ +--------------------------+
``` ```
Performed on [synapse with REST auth module](https://github.com/kamax-io/matrix-synapse-rest-auth/blob/master/README.md) Performed on [synapse with REST auth module](https://github.com/ma1uta/matrix-synapse-rest-password-provider/blob/master/README.md)
### Synapse ### Synapse
- Install the [password provider](https://github.com/kamax-io/matrix-synapse-rest-auth) - Install the [password provider](https://github.com/ma1uta/matrix-synapse-rest-password-provider)
- Edit your **synapse** configuration: - Edit your **synapse** configuration:
- As described by the auth module documentation - As described by the auth module documentation
- Set `endpoint` to `http://mxisdAddress:8090` - Replace `mxisdAddress` by an IP/host name that provides a direct - Set `endpoint` to `http://ma1sdAddress:8090` - Replace `ma1sdAddress` by an IP/host name that provides a direct
connection to mxisd. connection to ma1sd.
This **MUST NOT** be a public address, and SHOULD NOT go through a reverse proxy. This **MUST NOT** be a public address, and SHOULD NOT go through a reverse proxy.
- Restart synapse - Restart synapse
### mxisd ### ma1sd
- Configure and enable at least one [Identity store](../stores/README.md) - Configure and enable at least one [Identity store](../stores/README.md)
- Restart mxisd - Restart ma1sd
### Validate ### Validate
Login on the Homeserver using credentials present in one of your Identity stores. Login on the Homeserver using credentials present in one of your Identity stores.
@@ -93,7 +93,7 @@ This is performed by intercepting the Homeserver endpoint `/_matrix/client/r0/lo
| | Step 1 +---------------------------+ Step 2 | | Step 1 +---------------------------+ Step 2
| | | | | | | |
Client+---->| /_matrix/client/r0/login +---------------->| | Look up address +---------+ Client+---->| /_matrix/client/r0/login +---------------->| | Look up address +---------+
| ^ | | mxisd - Identity server +----------------->| Backend | | ^ | | ma1sd - Identity server +----------------->| Backend |
| | | | | +---------+ | | | | | +---------+
| /_matrix/* +--+ +---------------------+ | | /_matrix/* +--+ +---------------------+ |
| | | +---------------+-----------+ | | | +---------------+-----------+
@@ -110,7 +110,7 @@ Client+---->| /_matrix/client/r0/login +---------------->|
``` ```
Steps of user authentication using a 3PID: Steps of user authentication using a 3PID:
1. The intercepted login request is directly sent to mxisd instead of the Homeserver. 1. The intercepted login request is directly sent to ma1sd instead of the Homeserver.
2. Identity stores are queried for a matching user identity in order to modify the request to use the user name. 2. Identity stores are queried for a matching user identity in order to modify the request to use the user name.
3. The Homeserver, from which the request was intercepted, is queried using the request at previous step. 3. The Homeserver, from which the request was intercepted, is queried using the request at previous step.
Its address is resolved using the DNS Overwrite feature to reach its internal address on a non-encrypted port. Its address is resolved using the DNS Overwrite feature to reach its internal address on a non-encrypted port.
@@ -129,7 +129,7 @@ The specific configuration to put under the relevant `VirtualHost`:
```apache ```apache
ProxyPass /_matrix/client/r0/login http://localhost:8090/_matrix/client/r0/login ProxyPass /_matrix/client/r0/login http://localhost:8090/_matrix/client/r0/login
``` ```
`ProxyPreserveHost` or equivalent **must** be enabled to detect to which Homeserver mxisd should talk to when building results. `ProxyPreserveHost` or equivalent **must** be enabled to detect to which Homeserver ma1sd should talk to when building results.
Your VirtualHost should now look similar to: Your VirtualHost should now look similar to:
```apache ```apache
@@ -145,13 +145,58 @@ Your VirtualHost should now look similar to:
</VirtualHost> </VirtualHost>
``` ```
##### nginx
The specific configuration to add under the relevant `server`:
```nginx
location /_matrix/client/r0/login {
proxy_pass http://localhost:8090;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
}
```
Your `server` section should now look similar to:
```nginx
server {
listen 443 ssl;
server_name matrix.example.org;
# ...
location /_matrix/client/r0/login {
proxy_pass http://localhost:8090;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
}
location /_matrix/identity {
proxy_pass http://localhost:8090/_matrix/identity;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
}
location /_matrix {
proxy_pass http://localhost:8008/_matrix;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
}
}
```
#### DNS Overwrite #### DNS Overwrite
Just like you need to configure a reverse proxy to send client requests to mxisd, you also need to configure mxisd with
Just like you need to configure a reverse proxy to send client requests to ma1sd, you also need to configure ma1sd with
the internal IP of the Homeserver so it can talk to it directly to integrate its directory search. the internal IP of the Homeserver so it can talk to it directly to integrate its directory search.
To do so, put the following configuration in your mxisd configuration: To do so, put the following configuration in your ma1sd configuration:
```yaml ```yaml
dns.overwrite.homeserver.client: dns:
overwrite:
homeserver:
client:
- name: 'example.org' - name: 'example.org'
value: 'http://localhost:8008' value: 'http://localhost:8008'
``` ```
@@ -162,8 +207,14 @@ In case the hostname is the same as your Matrix domain and `server.name` is not
`value` is the base internal URL of the Homeserver, without any `/_matrix/..` or trailing `/`. `value` is the base internal URL of the Homeserver, without any `/_matrix/..` or trailing `/`.
### Optional features
The following features are available after you have a working Advanced setup:
- Username rewrite: Allows you to rewrite the username of a regular login/pass authentication to a 3PID, that then gets resolved using the regular lookup process. Most common use case is to allow login with numerical usernames on synapse, which is not possible out of the box.
#### Username rewrite #### Username rewrite
In mxisd config: In ma1sd config:
```yaml ```yaml
auth: auth:
rewrite: rewrite:

View File

@@ -1,19 +1,22 @@
# Bridge Integration # Bridge Integration
To help natural bridge integration into the regular usage of a Matrix client, mxisd provides a way for bridge to reply To help natural bridge integration into the regular usage of a Matrix client, ma1sd provides a way for bridge to reply
to 3PID queries if no mapping was found, allowing seamless bridging to a network. to 3PID queries if no mapping was found, allowing seamless bridging to a network.
This is performed by implementing a specific endpoint on the bridge to map a 3PID lookup to a virtual user. This is performed by implementing a specific endpoint on the bridge to map a 3PID lookup to a virtual user.
**NOTE**: This document is incomplete and might be misleading. In doubt, come in our Matrix room. **NOTE**: This document is incomplete and might be misleading. In doubt, come in our Matrix room.
You can also look at our [Email Bridge README](https://github.com/kamax-io/matrix-appservice-email#mxisd) for an example You can also look at our [Email Bridge README](https://github.com/kamax-matrix/matrix-appservice-email#ma1sd) for an example
of working configuration. of working configuration.
## Configuration ## Configuration
```yaml ```yaml
lookup.recursive.bridge.enabled: <boolean> lookup:
lookup.recursive.bridge.recursiveOnly: <boolean> recursive:
lookup.recursive.bridge.server: <URL to the bridge endpoint for all 3PID medium> bridge:
lookup.recursive.bridge.mappings: enabled: <boolean>
recursiveOnly: <boolean>
server: <URL to the bridge endpoint for all 3PID medium>
mappings:
<3PID MEDIUM HERE>: <URL to dedicated bridge for that medium> <3PID MEDIUM HERE>: <URL to dedicated bridge for that medium>
``` ```

View File

@@ -22,9 +22,9 @@ By enabling this feature, you can by default:
- Search for users which you are not in contact with yet. Super useful for corporations who want to give Matrix access - Search for users which you are not in contact with yet. Super useful for corporations who want to give Matrix access
internally, so users can just find themselves **prior** to having any common room(s) internally, so users can just find themselves **prior** to having any common room(s)
- Add extra attributes of your backend to extend the search - Add extra attributes of your backend to extend the search
- Include your homeserver search results to those found by mxisd - Include your homeserver search results to those found by ma1sd
By integrating mxisd, you get the default behaviour and a bunch of extras, ensuring your users will always find each other. By integrating ma1sd, you get the default behaviour and a bunch of extras, ensuring your users will always find each other.
## Overview ## Overview
This is performed by intercepting the Homeserver endpoint `/_matrix/client/r0/user_directory/search` like so: This is performed by intercepting the Homeserver endpoint `/_matrix/client/r0/user_directory/search` like so:
@@ -33,7 +33,7 @@ This is performed by intercepting the Homeserver endpoint `/_matrix/client/r0/us
Client --> | Reverse proxy Step 2 Client --> | Reverse proxy Step 2
| Step 1 +-------------------------+ | Step 1 +-------------------------+
| /_matrix/client/r0/user_directory/search ----------> | | Search in +---------+ | /_matrix/client/r0/user_directory/search ----------> | | Search in +---------+
| /\ | mxisd - Identity server | -----------> | Backend | | /\ | ma1sd - Identity server | -----------> | Backend |
| /_matrix/* \----------------------------- | | all users +---------+ | /_matrix/* \----------------------------- | | all users +---------+
| | Step 4: Send back merged results +-------------------------+ | | Step 4: Send back merged results +-------------------------+
+ | | + | |
@@ -44,7 +44,7 @@ Client --> | Reverse proxy
+------------+ /_matrix/client/r0/user_directory/search +------------+ /_matrix/client/r0/user_directory/search
``` ```
Steps: Steps:
1. The intercepted request is directly sent to mxisd instead of the Homeserver. 1. The intercepted request is directly sent to ma1sd instead of the Homeserver.
2. Identity stores are queried for any match on the search value sent by the client. 2. Identity stores are queried for any match on the search value sent by the client.
3. The Homeserver, from which the request was intercepted, is queried using the same request as the client. 3. The Homeserver, from which the request was intercepted, is queried using the same request as the client.
Its address is resolved using the DNS Overwrite feature to reach its internal address on a non-encrypted port. Its address is resolved using the DNS Overwrite feature to reach its internal address on a non-encrypted port.
@@ -52,7 +52,7 @@ Steps:
which directly answered the request. which directly answered the request.
## Requirements ## Requirements
- Reverse proxy setup, which you should already have in place if you use mxisd - Reverse proxy setup, which you should already have in place if you use ma1sd
- At least one compatible [Identity store](../stores/README.md) enabled - At least one compatible [Identity store](../stores/README.md) enabled
## Configuration ## Configuration
@@ -62,7 +62,7 @@ The specific configuration to put under the relevant `VirtualHost`:
```apache ```apache
ProxyPass /_matrix/client/r0/user_directory/ http://0.0.0.0:8090/_matrix/client/r0/user_directory/ ProxyPass /_matrix/client/r0/user_directory/ http://0.0.0.0:8090/_matrix/client/r0/user_directory/
``` ```
`ProxyPreserveHost` or equivalent must be enabled to detect to which Homeserver mxisd should talk to when building `ProxyPreserveHost` or equivalent must be enabled to detect to which Homeserver ma1sd should talk to when building
results. results.
Your `VirtualHost` should now look like this: Your `VirtualHost` should now look like this:
@@ -118,32 +118,36 @@ server {
``` ```
### DNS Overwrite ### DNS Overwrite
Just like you need to configure a reverse proxy to send client requests to mxisd, you also need to configure mxisd with Just like you need to configure a reverse proxy to send client requests to ma1sd, you also need to configure ma1sd with
the internal IP of the Homeserver so it can talk to it directly to integrate its directory search. the internal IP of the Homeserver so it can talk to it directly to integrate its directory search.
To do so, use the following configuration: To do so, use the following configuration:
```yaml ```yaml
dns.overwrite.homeserver.client: dns:
overwrite:
homeserver:
client:
- name: 'example.org' - name: 'example.org'
value: 'http://localhost:8008' value: 'http://localhost:8008'
``` ```
`name` must be the hostname of the URL that clients use when connecting to the Homeserver. - `name` must be the hostname of the URL that clients use when connecting to the Homeserver.
In case the hostname is the same as your Matrix domain, you can use `${matrix.domain}` to auto-populate the value using - `value` is the base internal URL of the Homeserver, without any `/_matrix/..` or trailing `/`.
the `matrix.domain` configuration option and avoid duplicating it.
`value` is the base internal URL of the Homeserver, without any `/_matrix/..` or trailing `/`.
## Next steps ## Next steps
### Homeserver results ### Homeserver results
You can configure if the Homeserver should be queried at all when doing a directory search. You can configure if the Homeserver should be queried at all when doing a directory search.
To disable Homeserver results, set the following in mxisd configuration file: To disable Homeserver results, set the following in ma1sd configuration file:
```yaml ```yaml
directory.exclude.homeserver: true directory:
exclude:
homeserver: true
``` ```
### 3PID exclusion in search ### 3PID exclusion in search
You can configure if the 3PID should also be included when doing a directory search. You can configure if the 3PID should also be included when doing a directory search.
By default, a search is performed on the 3PIDs. If you would like to not include them: By default, a search is performed on the 3PIDs. If you would like to not include them:
```yaml ```yaml
directory.exclude.threepid: true directory:
exclude:
threepid: true
``` ```

View File

@@ -1,72 +1,122 @@
# Integration as an Application Service # Application Service
**WARNING:** These features are currently highly experimental. They can be removed or modified without notice. **WARNING:** These features are currently highly experimental. They can be removed or modified without notice.
All the features requires a Homeserver capable of connecting Application Services. All the features requires a Homeserver capable of connecting [Application Services](https://matrix.org/docs/spec/application_service/r0.1.1.html).
## Email notification for Room invites by Matrix ID The following capabilities are provided in this feature:
- [Admin commands](#admin-commands)
- [Email Notification about room invites by Matrix IDs](#email-notification-about-room-invites-by-matrix-ids)
- [Auto-reject of expired 3PID invites](#auto-reject-of-expired-3pid-invites)
## Setup
> **NOTE:** Make sure you are familiar with [configuration format and rules](../../configure.md).
Integration as an Application service is a three steps process:
1. Create the baseline ma1sd configuration to allow integration.
2. Integrate with the homeserver.
3. Configure the specific capabilities, if applicable.
### Configuration
#### Variables
Under the `appsvc` namespace:
| Key | Type | Required | Default | Purpose |
|-----------------------|---------|----------|---------|----------------------------------------------------------------|
| `enabled` | boolean | No | `false` | Globally enable/disable the feature |
| `user.main` | string | No | `ma1sd` | Localpart for the main appservice user |
| `endpoint.toHS.url` | string | Yes | *None* | Base URL to the Homeserver |
| `endpoint.toHS.token` | string | Yes | *None* | Token to use when sending requests to the Homeserver |
| `endpoint.toAS.url` | string | Yes | *None* | Base URL to ma1sd from the Homeserver |
| `endpoint.toAS.token` | string | Yes | *None* | Token for the Homeserver to use when sending requests to ma1sd |
#### Example
```yaml
appsvc:
enabled: true
endpoint:
toHS:
url: 'http://localhost:8008'
token: 'ExampleTokenToHS-ChangeMe!'
toAS:
url: 'http://localhost:8090'
token: 'ExampleTokenToAS-ChangeMe!'
```
### Integration
#### Synapse
Under the `appsvc.registration.synapse` namespace:
| Key | Type | Required | Default | Purpose |
|--------|--------|----------|--------------------|--------------------------------------------------------------------------|
| `id` | string | No | `appservice-ma1sd` | The unique, user-defined ID of this application service. See spec. |
| `file` | string | Yes | *None* | If defined, the synapse registration file that should be created/updated |
##### Example
```yaml
appsvc:
registration:
synapse:
file: '/etc/matrix-synapse/ma1sd-appservice-registration.yaml'
```
Edit your `homeserver.yaml` and add a new entry to the appservice config file, which should look something like this:
```yaml
app_service_config_files:
- '/etc/matrix-synapse/ma1sd-appservice-registration.yaml'
- ...
```
Restart synapse when done to register ma1sd.
#### Others
See your Homeserver documentation on how to integrate.
## Capabilities
### Admin commands
#### Setup
Min config:
```yaml
appsvc:
feature:
admin:
allowedRoles:
- '+aMatrixCommunity:example.org'
- 'SomeLdapGroup'
- 'AnyOtherArbitraryRoleFromIdentityStores'
```
#### Use
The following steps assume:
- `matrix.domain` set to `example.org`
- `appsvc.user.main` set to `ma1sd` or not set
1. Invite `@ma1sd:example.org` to a new direct chat
2. Type `!help` to get all available commands
### Email Notification about room invites by Matrix IDs
This feature allows for users found in Identity stores to be instantly notified about Room Invites, regardless if their This feature allows for users found in Identity stores to be instantly notified about Room Invites, regardless if their
account was already provisioned on the Homeserver. account was already provisioned on the Homeserver.
### Requirements #### Requirements
- [Identity store(s)](../../stores/README.md) supporting the Profile feature - [Identity store(s)](../../stores/README.md) supporting the Profile feature
- At least one email entry in the identity store for each user that could be invited. - At least one email entry in the identity store for each user that could be invited.
### Configuration #### Configuration
In your mxisd config file: In your ma1sd config file:
```yaml ```yaml
matrix:
listener:
url: '<URL TO THE CS API OF THE HOMESERVER>'
localpart: 'appservice-mxisd'
token:
hs: 'HS_TOKEN_CHANGE_ME'
synapseSql: synapseSql:
enabled: false ## Do not use this line if Synapse is used as an Identity Store enabled: false ## Do not use this line if Synapse is used as an Identity Store
type: '<DB TYPE>' type: '<DB TYPE>'
connection: '<DB CONNECTION URL>' connection: '<DB CONNECTION URL>'
``` ```
The `synapseSql` section is used to retrieve display names which are not directly accessible in this mode. The `synapseSql` section is optional. It is used to retrieve display names which are not directly accessible in this mode.
For details about `type` and `connection`, see the [relevant documentation](../../stores/synapse.md). For details about `type` and `connection`, see the [relevant documentation](../../stores/synapse.md).
If you do not configure it, some placeholders will not be available in the notification, like the Room name. If you do not configure it, some placeholders will not be available in the notification, like the Room name.
You can also change the default template of the notification using the `generic.matrixId` template option. You can also change the default template of the notification using the `generic.matrixId` template option.
See [the Template generator documentation](../../threepids/notification/template-generator.md) for more info. See [the Template generator documentation](../../threepids/notification/template-generator.md) for more info.
### Homeserver integration #### Test
#### Synapse
Create a new appservice registration file. Futher config will assume it is in `/etc/matrix-synapse/appservice-mxisd.yaml`
```yaml
id: "appservice-mxisd"
url: "http://127.0.0.1:8090"
as_token: "AS_TOKEN_CHANGE_ME"
hs_token: "HS_TOKEN_CHANGE_ME"
sender_localpart: "appservice-mxisd"
namespaces:
users:
- regex: "@*"
exclusive: false
aliases: []
rooms: []
```
`id`: An arbitrary unique string to identify the AS.
`url`: mxisd to reach mxisd. This ideally should be HTTP and not going through any reverse proxy.
`as_token`: Arbitrary value used by mxisd when talking to the HS. Not currently used.
`hs_token`: Arbitrary value used by synapse when talking to mxisd. Must match `token.hs` in mxisd config.
`sender_localpart`: Username for the mxisd itself on the HS. Default configuration should be kept.
`namespaces`: To be kept as is.
Edit your `homeserver.yaml` and add a new entry to the appservice config file, which should look something like this:
```yaml
app_service_config_files:
- '/etc/matrix-synapse/appservice-mxisd.yaml'
- ...
```
Restart synapse when done to register mxisd.
#### Others
See your Homeserver documentation on how to integrate.
### Test
Invite a user which is part of your domain while an appropriate Identity store is used. Invite a user which is part of your domain while an appropriate Identity store is used.
### Auto-reject of expired 3PID invites
*TBC*

View File

@@ -5,7 +5,7 @@ Federated Identity server using the DNS domain part of the 3PID.
Emails are the best candidate for this kind of resolution which are DNS domain based already. Emails are the best candidate for this kind of resolution which are DNS domain based already.
On the other hand, Phone numbers cannot be resolved this way. On the other hand, Phone numbers cannot be resolved this way.
For 3PIDs which are not compatible with the DNS system, mxisd can be configured to talk to fallback Identity servers like For 3PIDs which are not compatible with the DNS system, ma1sd can be configured to talk to fallback Identity servers like
the central matrix.org one. See the [Identity feature](identity.md#lookups) for instructions on how to enable it. the central matrix.org one. See the [Identity feature](identity.md#lookups) for instructions on how to enable it.
Outbound federation is enabled by default while inbound federation is opt-in and require a specific DNS record. Outbound federation is enabled by default while inbound federation is opt-in and require a specific DNS record.
@@ -13,7 +13,7 @@ Outbound federation is enabled by default while inbound federation is opt-in and
## Overview ## Overview
``` ```
+-------------------+ +-------------> +----------+ +-------------------+ +-------------> +----------+
| mxisd | | | Backends | | ma1sd | | | Backends |
| | | +------> +----------+ | | | +------> +----------+
| | | | | | | |
| Invites / Lookups | | | | Invites / Lookups | | |
@@ -23,7 +23,7 @@ Outbound federation is enabled by default while inbound federation is opt-in and
| | | | | |
| +--------+ | | +-------------------+ | +--------+ | | +-------------------+
Homeserver --->| Local |>------------------+------> | Remote Federated | Homeserver --->| Local |>------------------+------> | Remote Federated |
and clients | +--------+ | | mxisd servers | and clients | +--------+ | | ma1sd servers |
+-------------------+ +-------------------+ +-------------------+ +-------------------+
``` ```
@@ -34,16 +34,18 @@ If you would like to be reachable for lookups over federation, create the follow
_matrix-identity._tcp.example.com. 3600 IN SRV 10 0 443 matrix.example.com. _matrix-identity._tcp.example.com. 3600 IN SRV 10 0 443 matrix.example.com.
``` ```
The port must be HTTPS capable which is what you get in a regular setup with a reverse proxy from 443 to TCP 8090 of mxisd. The port must be HTTPS capable which is what you get in a regular setup with a reverse proxy from 443 to TCP 8090 of ma1sd.
## Outbound ## Outbound
If you would like to disable outbound federation and isolate your identity server from the rest of the Matrix network, If you would like to disable outbound federation and isolate your identity server from the rest of the Matrix network,
use the following mxisd configuration options: use the following ma1sd configuration options:
```yaml ```yaml
lookup.recursive.enabled: false lookup:
invite.resolution.recursive: false recursive:
session.policy.validation.forLocal.toRemote.enabled: false enabled: false
session.policy.validation.forRemote.toRemote.enabled: false invite:
resolution:
recursive: false
``` ```
There is currently no way to selectively disable federation towards specific servers, but this feature is planned. There is currently no way to selectively disable federation towards specific servers, but this feature is planned.

View File

@@ -1,20 +1,96 @@
# Identity # Identity
**WARNING**: This document is incomplete and can be missleading. Implementation of the [Identity Service API r0.2.0](https://matrix.org/docs/spec/identity_service/r0.2.0.html).
Implementation of the [Unofficial Matrix Identity Service API](https://kamax.io/matrix/api/identity_service/unstable.html). - [Lookups](#lookups)
- [Invitations](#invitations)
- [Expiration](#expiration)
- [Policies](#policies)
- [Resolution](#resolution)
- [3PIDs Management](#3pids-management)
## Lookups ## Lookups
If you would like to use the central matrix.org Identity server to ensure maximum discovery at the cost of potentially If you would like to use the central matrix.org Identity server to ensure maximum discovery at the cost of potentially
leaking all your contacts information, add the following to your configuration: leaking all your contacts information, add the following to your configuration:
```yaml ```yaml
forward.servers: forward:
servers:
- 'matrix-org' - 'matrix-org'
``` ```
**NOTE:** You should carefully consider enabling this option, which is discouraged. **NOTE:** You should carefully consider enabling this option, which is discouraged.
For more info, see the [relevant issue](https://github.com/kamax-matrix/mxisd/issues/76). For more info, see the [relevant issue](https://github.com/kamax-matrix/ma1sd/issues/76).
## Room Invitations ## Invitations
Resolution can be customized using the following configuration: ### Expiration
#### Overview
Matrix does not provide a mean to remove/cancel pending 3PID invitations with the APIs. The current reference
implementations also do not provide any mean to do so. This leads to 3PID invites forever stuck in rooms.
To provide this functionality, ma1sd uses a workaround: resolve the invite to a dedicated User ID, which can be
controlled by ma1sd or a bot/service that will then reject the invite.
If this dedicated User ID is to be controlled by ma1sd, the [Application Service](experimental/application-service.md)
feature must be configured and integrated with your Homeserver, as well as the *Auto-reject 3PID invite capability*.
#### Configuration
```yaml
invite:
expiration:
enabled: true/false
after: 5
resolveTo: '@john.doe:example.org'
```
`enabled`
- Purpose: Enable or disable the invite expiration feature.
- Default: `true`
`after`
- Purpose: Amount of minutes before an invitation expires.
- Default: `10080` (7 days)
`resolveTo`
- Purpose: Matrix User ID to resolve the expired invitations to.
- Default: Computed from `appsvc.user.inviteExpired` and `matrix.domain`
### Policies
3PID invite policies are the companion feature of [Registration](registration.md). While the Registration feature acts on
requirements for the invitee/register, this feature acts on requirement for the one(s) performing 3PID invites, ensuring
a coherent system.
It relies on only allowing people with specific [Roles](profile.md) to perform 3PID invites. This would typically allow
a tight-control on a server setup with is "invite-only" or semi-open (relying on trusted people to invite new members).
It's a middle ground between a closed server, where every user must be created or already exists in an Identity store,
and an open server, where anyone can register.
#### Integration
Because Identity Servers do not control 3PID invites as per Matrix spec, ma1sd needs to intercept a set of Homeserver
endpoints to apply the policies.
##### Reverse Proxy
###### nginx
**IMPORTANT**: Must be placed before your global `/_matrix` entry:
```nginx
location ~* ^/_matrix/client/r0/rooms/([^/]+)/invite$ {
proxy_pass http://127.0.0.1:8090;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
}
```
#### Configuration
The only policy currently available is to restrict 3PID invite to users having a specific (set of) role(s), like so:
```yaml
invite:
policy:
ifSender:
hasRole:
- '<THIS_ROLE>'
- '<OR_THIS_ROLE>'
```
### Resolution
Resolution of 3PID invitations can be customized using the following configuration:
`invite.resolution.recursive` `invite.resolution.recursive`
- Default value: `true` - Default value: `true`
@@ -25,7 +101,7 @@ Resolution can be customized using the following configuration:
`invite.resolution.timer` `invite.resolution.timer`
- Default value: `1` - Default value: `1`
- Description: How often, in minutes, mxisd should try to resolve pending invites. - Description: How often, in minutes, ma1sd should try to resolve pending invites.
## 3PID addition to user profile ## 3PIDs Management
See the [3PID session documents](../threepids/session) See the [3PID session documents](../threepids/session)

View File

@@ -0,0 +1,111 @@
# Registration
- [Overview](#overview)
- [Integration](#integration)
- [Reverse Proxy](#reverse-proxy)
- [nginx](#nginx)
- [Apache2](#apache2)
- [Homeserver](#homeserver)
- [synapse](#synapse)
- [Configuration](#configuration)
- [Example](#example)
- [Usage](#usage)
## Overview
**NOTE**: This feature is beta: it is considered stable enough for production but is incomplete and may contain bugs.
Registration is an enhanced feature of ma1sd to control registrations involving 3PIDs on a Homeserver based on policies:
- Match pending 3PID invites on the server
- Match 3PID pattern, like a specific set of domains for emails
- In further releases, use 3PIDs found in Identity stores
It aims to help open or invite-only registration servers control what is possible to do and ensure only approved people
can register on a given server in a implementation-agnostic manner.
**IMPORTANT:** This feature does not control registration in general. It only acts on endpoints related to 3PIDs during
the registration process.
As such, it relies on the homeserver to require 3PIDs with the registration flows.
This feature is not part of the Matrix Identity Server spec.
## Integration
ma1sd needs to be integrated at several levels for this feature to work:
- Reverse proxy: intercept the 3PID register endpoints and act on them
- Homeserver: require 3PID to be part of the registration data
Later version(s) of this feature may directly control registration itself to create a coherent experience
### Reverse Proxy
#### nginx
```nginx
location ~* ^/_matrix/client/r0/register/[^/]+/requestToken$ {
proxy_pass http://localhost:8090;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
}
```
#### Apache2
> TBC
### Homeserver
#### Synapse
```yaml
enable_registration: true
registrations_require_3pid:
- email
```
## Configuration
See the [Configuration](../configure.md) introduction doc on how to read the configuration keys.
An example of working configuration is available at the end of this section.
### Enable/Disable
`register.allowed`, taking a boolean, can be used to enable/disable registration if the attempt is not 3PID-based.
`false` is the default value to prevent open registration, as you must allow it on the homeserver side.
### For invites
`register.invite`, taking a boolean, controls if registration can be made using a 3PID which matches a pending 3PID invite.
`true` is the default value.
### 3PID-specific
At this time, only `email` is supported with 3PID specific configuration with this feature.
#### Email
**Base key**: `register.threepid.email`
##### Domain whitelist/blacklist
If you would like to control which domains are allowed to be used when registering with an email, the following sub-keys
are available:
- `domain.whitelist`
- `domain.blacklist`
The value format is an hybrid between glob patterns and postfix configuration files with the following syntax:
- `*<domain>` will match the domain and any sub-domain(s)
- `.<domain>` will only match sub-domain(s)
- `<domain>` will only match the exact domain
The following table illustrates pattern and matching status against example values:
| Config value | Matches `example.org` | Matches `sub.example.org` |
|--------------- |-----------------------|---------------------------|
| `*example.org` | Yes | Yes |
| `.example.org` | No | Yes |
| `example.org` | Yes | No |
### Example
For the following example configuration:
```yaml
register:
policy:
threepid:
email:
domain:
whitelist:
- '*example.org'
- '.example.net'
- 'example.com'
```
- Users can register using 3PIDs of pending invites, being allowed by default.
- Users can register using an email from `example.org` and any sub-domain, only sub-domains of `example.net` and `example.com` but not its sub-domains.
- Otherwise, user registration will be denied.
## Usage
Nothing special is needed. Register using a regular Matrix client.

View File

@@ -6,24 +6,36 @@
5. [Validate](#validate) 5. [Validate](#validate)
6. [Next steps](#next-steps) 6. [Next steps](#next-steps)
Following these quick start instructions, you will have a basic setup that can perform recursive/federated lookups and Following these quick start instructions, you will have a basic setup that can perform recursive/federated lookups.
talk to the central Matrix.org Identity server.
This will be a good ground work for further integration with features and your existing Identity stores. This will be a good ground work for further integration with features and your existing Identity stores.
---
If you would like a more fully integrated setup out of the box, the [matrix-docker-ansible-deploy](https://github.com/spantaleev/matrix-docker-ansible-deploy)
project provides a turn-key full-stack solution, including LDAP and the various ma1sd features enabled and ready.
We work closely with the project owner so the latest ma1sd version is always supported.
If you choose to use it, this Getting Started guide is not applicable - See the project documentation. You may then
directly go to the [Next steps](#next-steps).
## Preparation ## Preparation
You will need: You will need:
- Working Homeserver, ideally with working federation - Working Homeserver, ideally with working federation
- Reverse proxy with regular TLS/SSL certificate (Let's encrypt) for your mxisd domain - Reverse proxy with regular TLS/SSL certificate (Let's encrypt) for your ma1sd domain
As synapse requires an HTTPS connection when talking to an Identity service, **a reverse proxy is required** as mxisd does If you use synapse:
not support HTTPS listener at this time. - It requires an HTTPS connection when talking to an Identity service, **a reverse proxy is required** as ma1sd does
not support HTTPS listener at this time.
- HTTPS is hardcoded when talking to the Identity server. If your Identity server URL in your client is `https://matrix.example.org/`,
then you need to ensure `https://matrix.example.org/_matrix/identity/api/v1/...` will reach ma1sd if called from the synapse host.
In doubt, test with `curl` or similar.
For maximum integration, it is best to have your Homeserver and mxisd reachable via the same hostname. For maximum integration, it is best to have your Homeserver and ma1sd reachable via the same public hostname.
Be aware of a [NAT/Reverse proxy gotcha](https://github.com/kamax-matrix/mxisd/wiki/Gotchas#nating) if you use the same Be aware of a [NAT/Reverse proxy gotcha](https://github.com/ma1uta/ma1sd/wiki/Gotchas#nating) if you use the same
hostname. host.
The following Quick Start guide assumes you will host the Homeserver and mxisd under the same hostname. The following Quick Start guide assumes you will host the Homeserver and ma1sd under the same hostname.
If you would like a high-level view of the infrastructure and how each feature is integrated, see the If you would like a high-level view of the infrastructure and how each feature is integrated, see the
[dedicated document](architecture.md) [dedicated document](architecture.md)
@@ -35,30 +47,30 @@ Install via:
- [NixOS](install/nixos.md) - [NixOS](install/nixos.md)
- [Sources](build.md) - [Sources](build.md)
See the [Latest release](https://github.com/kamax-matrix/mxisd/releases/latest) for links to each. See the [Latest release](https://github.com/ma1uta/ma1sd/releases/latest) for links to each.
## Configure ## Configure
**NOTE**: please view the install instruction for your platform, as this step might be optional or already handled for you. > **NOTE**: Please view the install instruction for your platform, as this step might be optional or already handled for you.
Create/edit a minimal configuration (see installer doc for the location): > **NOTE**: Details about configuration syntax and format are described [here](configure.md)
```yaml
matrix.domain: 'example.org' If you haven't created a configuration file yet, copy `ma1sd.example.yaml` to where the configuration file is stored given
key.path: '/path/to/signing.key.file' your installation method and edit to your needs.
storage.provider.sqlite.database: '/path/to/mxisd.db'
``` The following items must be at least configured:
- `matrix.domain` should be set to your Homeserver domain (`server_name` in synapse configuration) - `matrix.domain` should be set to your Homeserver domain (`server_name` in synapse configuration)
- `key.path` will store the signing keys, which must be kept safe! If the file does not exist, keys will be generated for you. - `key.path` will store the signing keys, which must be kept safe! If the file does not exist, keys will be generated for you.
- `storage.provider.sqlite.database` is the location of the SQLite Database file which will hold state (invites, etc.) - `storage.provider.sqlite.database` is the location of the SQLite Database file which will hold state (invites, etc.)
If your HS/mxisd hostname is not the same as your Matrix domain, configure `server.name`. If your HS/ma1sd hostname is not the same as your Matrix domain, configure `server.name`.
Complete configuration guide is available [here](configure.md). Complete configuration guide is available [here](configure.md).
## Integrate ## Integrate
For an overview of a typical mxisd infrastructure, see the [dedicated document](architecture.md) For an overview of a typical ma1sd infrastructure, see the [dedicated document](architecture.md)
### Reverse proxy ### Reverse proxy
#### Apache2 #### Apache2
In the `VirtualHost` section handling the domain with SSL, add the following and replace `0.0.0.0` by the internal In the `VirtualHost` section handling the domain with SSL, add the following and replace `0.0.0.0` by the internal
hostname/IP pointing to mxisd. hostname/IP pointing to ma1sd.
**This line MUST be present before the one for the homeserver!** **This line MUST be present before the one for the homeserver!**
```apache ```apache
ProxyPass /_matrix/identity http://0.0.0.0:8090/_matrix/identity ProxyPass /_matrix/identity http://0.0.0.0:8090/_matrix/identity
@@ -67,9 +79,9 @@ ProxyPass /_matrix/identity http://0.0.0.0:8090/_matrix/identity
Typical configuration would look like: Typical configuration would look like:
```apache ```apache
<VirtualHost *:443> <VirtualHost *:443>
ServerName example.org ServerName matrix.example.org
... # ...
ProxyPreserveHost on ProxyPreserveHost on
ProxyPass /_matrix/identity http://localhost:8090/_matrix/identity ProxyPass /_matrix/identity http://localhost:8090/_matrix/identity
@@ -79,7 +91,7 @@ Typical configuration would look like:
#### nginx #### nginx
In the `server` section handling the domain with SSL, add the following and replace `0.0.0.0` with the internal In the `server` section handling the domain with SSL, add the following and replace `0.0.0.0` with the internal
hostname/IP pointing to mxisd. hostname/IP pointing to ma1sd.
**This line MUST be present before the one for the homeserver!** **This line MUST be present before the one for the homeserver!**
```nginx ```nginx
location /_matrix/identity { location /_matrix/identity {
@@ -91,9 +103,9 @@ Typical configuration would look like:
```nginx ```nginx
server { server {
listen 443 ssl; listen 443 ssl;
server_name example.org; server_name matrix.example.org;
... # ...
location /_matrix/identity { location /_matrix/identity {
proxy_pass http://localhost:8090/_matrix/identity; proxy_pass http://localhost:8090/_matrix/identity;
@@ -110,33 +122,36 @@ server {
``` ```
### Synapse ### Synapse
Add your mxisd 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:
- example.org - matrix.example.org
``` ```
It is recommended to remove `matrix.org` and `vector.im` (or any other default entry) from your configuration so only It is **highly recommended** to remove `matrix.org` and `vector.im` (or any other default entry) from your configuration
your own Identity server is authoritative for your HS. so only your own Identity server is authoritative for your HS.
## Validate ## 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
your installation validated. your installation validated.
1. Log in using your Matrix client and set `https://example.org` as your Identity server URL, replacing `example.org` by 1. Log in using your Matrix client and set `https://matrix.example.org` as your Identity server URL, replacing `matrix.example.org`
the relevant hostname which you configured in your reverse proxy. by the relevant hostname which you configured in your reverse proxy.
2. Create a new empty room. All further actions will take place in this room. 2. Create a new empty room. All further actions will take place in this room.
3. Invite `mxisd-federation-test@kamax.io` 3. Invite `ma1sd-federation-test@kamax.io`
4. The 3PID invite should be turned into a Matrix invite to `@mxisd-lookup-test:kamax.io`. 4. The 3PID invite should be turned into a Matrix invite to `@ma1sd-lookup-test:kamax.io`.
5. The invited test user will join the room, send a congratulation message and leave. 5. The invited test user will join the room, send a congratulation message and leave.
**NOTE:** You might not see a suggestion for the e-mail address, which is normal. Still proceed with the invite. **NOTE:** You might not see a suggestion for the e-mail address, which is normal. Still proceed with the invite.
If it worked, it means you are up and running and can enjoy mxisd in its basic mode! Congratulations! If it worked, it means you are up and running and can enjoy ma1sd in its basic mode! Congratulations!
If it did not work, [get in touch](../README.md#support) and we'll do our best to get you started. If it did not work, read the basic [troubleshooting guide](troubleshooting.md), [get in touch](../README.md#support) and
we'll do our best to get you started.
## Next steps ## Next steps
Once your mxisd server is up and running, there are several ways you can enhance and integrate further with your Once your ma1sd server is up and running, there are several ways you can enhance and integrate further with your
infrastructure: infrastructure:
- [Enable extra features](features/) - [Enable extra features](features/)
- [Use your own Identity stores](stores/README.md) - [Use your own Identity stores](stores/README.md)
- [Hardening your ma1sd installation](install/security.md)
- [Learn about day-to-day operations](operations.md)

View File

@@ -1,3 +1,9 @@
---
** Outdated due to migrating to fork. **
---
# Arch Linux package # Arch Linux package
An Arch Linux package in the AUR repos is maintained by [r3pek](https://matrix.to/#/@r3pek:r3pek.org), a community member. An Arch Linux package in the AUR repos is maintained by [r3pek](https://matrix.to/#/@r3pek:r3pek.org), a community member.
See https://aur.archlinux.org/packages/mxisd/ See https://aur.archlinux.org/packages/mxisd/

View File

@@ -3,40 +3,40 @@
- Any distribution that supports Java 8 - Any distribution that supports Java 8
## Install ## Install
1. Download the [latest release](https://github.com/kamax-matrix/mxisd/releases/latest) 1. Download the [latest release](https://github.com/ma1uta/ma1sd/releases/latest)
2. Run: 2. Run:
```bash ```bash
dpkg -i /path/to/downloaded/mxisd.deb dpkg -i /path/to/downloaded/ma1sd.deb
``` ```
## Files ## Files
| Location | Purpose | | Location | Purpose |
|-------------------------------------|----------------------------------------------| |-------------------------------------|----------------------------------------------|
| `/etc/mxisd` | Configuration directory | | `/etc/ma1sd` | Configuration directory |
| `/etc/mxisd/mxisd.yaml` | Main configuration file | | `/etc/ma1sd/ma1sd.yaml` | Main configuration file |
| `/etc/systemd/system/mxisd.service` | Systemd configuration file for mxisd service | | `/etc/systemd/system/ma1sd.service` | Systemd configuration file for ma1sd service |
| `/usr/lib/mxisd` | Binaries | | `/usr/lib/ma1sd` | Binaries |
| `/var/lib/mxisd` | Data | | `/var/lib/ma1sd` | Data |
| `/var/lib/mxisd/signing.key` | Default location for mxisd signing keys | | `/var/lib/ma1sd/signing.key` | Default location for ma1sd signing keys |
## Control ## Control
Start mxisd using: Start ma1sd using:
```bash ```bash
sudo systemctl start mxisd sudo systemctl start ma1sd
``` ```
Stop mxisd using: Stop ma1sd using:
```bash ```bash
sudo systemctl stop mxisd sudo systemctl stop ma1sd
``` ```
## Troubleshoot ## Troubleshoot
All logs are sent to `STDOUT` which are saved in `/var/log/syslog` by default. All logs are sent to `STDOUT` which are saved in `/var/log/syslog` by default.
You can: You can:
- grep & tail using `mxisd`: - grep & tail using `ma1sd`:
``` ```
tail -n 99 -f /var/log/syslog | grep mxisd tail -n 99 -f /var/log/syslog | grep ma1sd
``` ```
- use Systemd's journal: - use Systemd's journal:
``` ```
journalctl -f -n 99 -u mxisd journalctl -f -n 99 -u ma1sd
``` ```

View File

@@ -1,13 +1,19 @@
---
** Outdated due to migrating to fork. **
---
# Docker # Docker
## Fetch ## Fetch
Pull the latest stable image: Pull the latest stable image:
```bash ```bash
docker pull kamax/mxisd docker pull ma1uta/ma1sd
``` ```
## Configure ## Configure
On first run, simply using `MATRIX_DOMAIN` as an environment variable will create a default config for you. On first run, simply using `MATRIX_DOMAIN` as an environment variable will create a default config for you.
You can also provide a configuration file named `mxisd.yaml` in the volume mapped to `/etc/mxisd` before starting your You can also provide a configuration file named `ma1sd.yaml` in the volume mapped to `/etc/ma1sd` before starting your
container. container.
## Run ## Run
@@ -16,7 +22,10 @@ Use the following command after adapting to your needs:
- The volumes host paths - The volumes host paths
```bash ```bash
docker run --rm -e MATRIX_DOMAIN=example.org -v /data/mxisd/etc:/etc/mxisd -v /data/mxisd/var:/var/mxisd -p 8090:8090 -t kamax/mxisd docker run --rm -e MATRIX_DOMAIN=example.org -v /data/ma1sd/etc:/etc/ma1sd -v /data/ma1sd/var:/var/ma1sd -p 8090:8090 -t ma1uta/ma1sd
``` ```
For more info, including the list of possible tags, see [the public repository](https://hub.docker.com/r/kamax/mxisd/) For more info, including the list of possible tags, see [the public repository](https://hub.docker.com/r/ma1uta/ma1sd/)
## Troubleshoot
Please read the [Troubleshooting guide](../troubleshooting.md).

View File

@@ -1,3 +1,9 @@
---
** Outdated due to migrating to fork. **
---
# NixOS package # NixOS package
mxisd is available as a NixOS package in the official repos. mxisd is available as a NixOS package in the official repos.

30
docs/install/security.md Normal file
View File

@@ -0,0 +1,30 @@
# Security hardening
## Overview
This document outlines the various operations you may want to perform to increase the security of your installation and
avoid leak of credentials/key pairs
## Configuration
Your config file should have the following ownership:
- Dedicated user for ma1sd, used to run the software
- Dedicated group for ma1sd, used by other applications to access and read configuration files
Your config file should have the following access:
- Read and write for the ma1sd user
- Read for the ma1sd group
- Nothing for others
This translates into `640` and be applied with `chmod 640 /path/to/config/file.yaml`.
## Data
The only sensible place is the key store where ma1sd's signing keys are stored. You should therefore limit access to only
the ma1sd user, and deny access to anything else.
Your key store should have the following access:
- Read and write for the ma1sd user
- Nothing for the ma1sd group
- Nothing for others
The identity store can either be a file or a directory, depending on your version. v1.4 and higher are using a directory,
everything before is using a file.
- If your version is directory-based, you will want to apply chmod `700` on it.
- If your version is file-based, you will want to apply chmod `600` on it.

View File

@@ -2,37 +2,43 @@
## Instructions ## Instructions
Follow the [build instructions](../build.md) then: Follow the [build instructions](../build.md) then:
1. Prepare files and directories: ### Prepare files and directories:
```bash ```bash
# Create a dedicated user # Create a dedicated user
useradd -r mxisd useradd -r ma1sd
# Create bin directory # Create config directory
mkdir /opt/mxisd mkdir -p /etc/ma1sd
# Create config directory and set ownership
mkdir -p /etc/opt/mxisd
chown -R mxisd /etc/opt/mxisd
# Create data directory and set ownership # Create data directory and set ownership
mkdir -p /var/opt/mxisd mkdir -p /var/lib/ma1sd
chown -R mxisd /var/opt/mxisd chown -R ma1sd /var/lib/ma1sd
# Copy <repo root>/build/libs/mxisd.jar to bin directory # Create bin directory, copy the jar and launch scriot to bin directory
cp ./build/libs/mxisd.jar /opt/mxisd/ mkdir /usr/lib/ma1sd
chown mxisd /opt/mxisd/mxisd.jar cp ./build/libs/ma1sd.jar /usr/lib/ma1sd/
chmod a+x /opt/mxisd/mxisd.jar cp ./src/script/ma1sd /usr/lib/ma1sd
chown -R ma1sd /usr/lib/ma1sd
chmod a+x /usr/lib/ma1sd/ma1sd
# Create symlink for easy exec # Create symlink for easy exec
ln -s /opt/mxisd/mxisd.jar /usr/bin/mxisd ln -s /usr/lib/ma1sd/ma1sd /usr/bin/ma1sd
``` ```
2. Copy the sample config file `./application.example.yaml` to `/etc/opt/mxisd/mxisd.yaml`, edit to your needs
3. Copy `src/systemd/mxisd.service` to `/etc/systemd/system/` and edit if needed ### Prepare config file
4. Enable service for auto-startup Copy the configuration file you've created following the build instructions to `/etc/ma1sd/ma1sd.yaml`
### Prepare Systemd
1. Copy `src/systemd/ma1sd.service` to `/etc/systemd/system/` and edit if needed
2. Enable service for auto-startup
```bash ```bash
systemctl enable mxisd systemctl enable ma1sd
``` ```
5. Start mxisd
### Run
```bash ```bash
systemctl start mxisd systemctl start ma1sd
``` ```
## Debug
ma1sd logs to stdout, which is normally sent to `/var/log/syslog` or `/var/log/messages`.

View File

@@ -0,0 +1,16 @@
# Migration from mxisd
Version 2.0.0 of the ma1sd uses the same format of the database schema and main configuration file as mxisd.
Migration from mxisd:
- install ma1sd via deb package, docker image or zip/tar archive
- stop mxisd
- copy configuration file (by default /etc/mxisd/mxisd.yaml to /etc/ma1sd/ma1sd.yaml)
- copy key store (by default /var/lib/mxisd/keys folder to /var/lib/ma1sd/keys)
- copy storage (by default /var/lib/mxisd/store.db to /var/lib/ma1sd/store.db)
- change paths in the new config file (ma1sd.yaml). There are options: `key.path` and `storage.provider.sqlite`
- start ma1sd
Due to ma1sd uses the same ports by default as mxisd it isn't necessary to change nginx/apache configuration.
If you have any troubles with migration don't hesitate to ask questions in [#ma1sd:ru-matrix.org](https://matrix.to/#/#ma1sd:ru-matrix.org) room.

21
docs/operations.md Normal file
View File

@@ -0,0 +1,21 @@
# Operations Guide
- [Overview](#overview)
- [Maintenance](#maintenance)
- [Backuo](#backup)
## Overview
This document gives various information for the day-to-day management and operations of ma1sd.
## Maintenance
ma1sd does not require any maintenance task to run at optimal performance.
## Backup
### Run
ma1sd requires all file in its configuration and data directory to be backed up.
They are usually located at:
- `/etc/ma1sd`
- `/var/lib/ma1sd`
### Restore
Reinstall ma1sd, restore the two folders above in the appropriate location (depending on your install method) and you
will be good to go. Simply start ma1sd to restore functionality.

View File

@@ -39,10 +39,10 @@
| [Authentication](../features/authentication.md) | Yes | | [Authentication](../features/authentication.md) | Yes |
| [Directory](../features/directory.md) | Yes | | [Directory](../features/directory.md) | Yes |
| [Identity](../features/identity.md) | Yes | | [Identity](../features/identity.md) | Yes |
| [Profile](#profile) | Yes | | [Profile](../features/profile.md) | Yes |
This Identity Store lets you run arbitrary commands to handle the various requests in each support feature. This Identity Store lets you run arbitrary commands to handle the various requests in each support feature.
It is the most versatile Identity store of mxisd, allowing you to connect any kind of logic with any executable/script. It is the most versatile Identity store of ma1sd, allowing you to connect any kind of logic with any executable/script.
## Overview ## Overview
Each request can be mapping to a fully customizable command configuration. Each request can be mapping to a fully customizable command configuration.
@@ -68,7 +68,8 @@ We will use the term `Executable` for each lookup/action and `Processor` for eac
### Global ### Global
```yaml ```yaml
exec.enabled: <boolean> exec:
enabled: <boolean>
``` ```
Enable/disable the Identity store at a global/default level. Each feature can still be individually enabled/disabled. Enable/disable the Identity store at a global/default level. Each feature can still be individually enabled/disabled.
@@ -79,7 +80,9 @@ Not all features use all tokens, and each feature might also have its own specif
They can be set within the following scope: They can be set within the following scope:
```yaml ```yaml
exec.token.<token>: '<value>' exec:
token:
<token>: '<value>'
``` ```
--- ---
@@ -184,16 +187,19 @@ The following types are available:
### Examples ### Examples
#### Basic #### Basic
```yaml ```yaml
exec.auth.enabled: true exec:
exec.auth.command: '/opt/mxisd-exec/auth.sh' auth:
exec.auth.args: ['{localpart}'] enabled: true
exec.auth.input.type: 'plain' command: '/opt/ma1sd-exec/auth.sh'
exec.auth.input.template: '{password}' args: ['{localpart}']
exec.auth.env: input:
type: 'plain'
template: '{password}'
env:
DOMAIN: '{domain}' DOMAIN: '{domain}'
``` ```
With Authentication enabled, run `/opt/mxisd-exec/auth.sh` when validating credentials, providing: With Authentication enabled, run `/opt/ma1sd-exec/auth.sh` when validating credentials, providing:
- A single command-line argument to provide the `localoart` as username - A single command-line argument to provide the `localpart` as username
- A plain text string with the password token for standard input, which will be replaced by the password to check - A plain text string with the password token for standard input, which will be replaced by the password to check
- A single environment variable `DOMAIN` containing Matrix ID domain, if given - A single environment variable `DOMAIN` containing Matrix ID domain, if given
@@ -201,26 +207,34 @@ The command will use the default values for:
- Success exit status of `0` - Success exit status of `0`
- Failure exit status of `1` - Failure exit status of `1`
- Any other exit status considered as error - Any other exit status considered as error
- The standard output processing as not processed - Standard output will not be processed
#### Advanced #### Advanced
Given the fictional `placeholder` feature: Given the fictional `placeholder` feature:
```yaml ```yaml
exec.enabled: true exec:
exec.token.mxid: '{matrixId}' enabled: true
token:
exec.placeholder.token.localpart: '{username}' mxid: '{matrixId}'
exec.placeholder.command: '/path/to/executable' auth:
exec.placeholder.args: token:
localpart: '{username}'
command: '/path/to/executable'
args:
- '-u' - '-u'
- '{username}' - '{username}'
exec.placeholder.env: env:
MATRIX_DOMAIN: '{domain}' MATRIX_DOMAIN: '{domain}'
MATRIX_USER_ID: '{matrixId}' MATRIX_USER_ID: '{matrixId}'
output:
exec.placeholder.output.type: 'json' type: 'json'
exec.placeholder.exit.success: [0, 128] exit:
exec.placeholder.exit.failure: [1, 129] success:
- 0
- 128
failure:
- 1
- 129
``` ```
With: With:
- The Identity store enabled for all features - The Identity store enabled for all features
@@ -243,14 +257,17 @@ See each dedicated [Feature](#features) section.
## Authentication ## Authentication
The Authentication feature can be enabled/disabled using: The Authentication feature can be enabled/disabled using:
```yaml ```yaml
exec.auth.enabled: <true/false> exec:
auth:
enabled: <true/false>
``` ```
--- ---
This feature provides a single *Executable* under the namespace: This feature provides a single *Executable* under the namespace:
```yaml ```yaml
exec.auth: exec:
auth:
... ...
``` ```
@@ -294,7 +311,9 @@ Default template:
## Directory ## Directory
The Directory feature can be enabled/disabled using: The Directory feature can be enabled/disabled using:
```yaml ```yaml
exec.directory.enabled: <true/false> exec:
directory:
enabled: <true/false>
``` ```
--- ---
@@ -303,12 +322,18 @@ Two search types configuration namespace are available, using the same input/out
By name: By name:
```yaml ```yaml
exec.directory.search.byName: exec:
directory:
search:
byName:
... ...
``` ```
By 3PID: By 3PID:
```yaml ```yaml
exec.directory.search.byThreepid: exec:
directory:
search:
byThreepid:
... ...
``` ```
@@ -386,7 +411,10 @@ The User ID type will default to `localpart` if:
### Bulk lookup ### Bulk lookup
Configuration namespace: Configuration namespace:
```yaml ```yaml
exec.identity.lookup.bulk: exec:
identity:
lookup:
bulk:
... ...
``` ```
@@ -418,7 +446,9 @@ Same as the [REST Identity Store](rest.md).
## Profile ## Profile
The Profile feature can be enabled/disabled using: The Profile feature can be enabled/disabled using:
```yaml ```yaml
exec.profile.enabled: <true/false> exec:
profile:
enabled: <true/false>
``` ```
--- ---
@@ -427,19 +457,25 @@ The following *Executable*s namespace are available, share the same input/output
Get Display name: Get Display name:
```yaml ```yaml
exec.profile.displayName: exec:
profile:
displayName:
... ...
``` ```
Get 3PIDs: Get 3PIDs:
```yaml ```yaml
exec.profile.threePid: exec:
profile:
threePid:
... ...
``` ```
Get Roles: Get Roles:
```yaml ```yaml
exec.profile.role: exec:
profile:
role:
... ...
``` ```

View File

@@ -2,12 +2,12 @@
https://firebase.google.com/ https://firebase.google.com/
## Features ## Features
| Name | Supported? | | Name | Supported |
|----------------|------------| |-------------------------------------------------|-----------|
| Authentication | Yes | | [Authentication](../features/authentication.md) | Yes |
| Directory | No | | [Directory](../features/directory.md) | No |
| Identity | Yes | | [Identity](../features/identity.md) | Yes |
| Profile | No | | [Profile](../features/profile.md) | No |
## Requirements ## Requirements
This backend requires a suitable Matrix client capable of performing Firebase authentication and passing the following This backend requires a suitable Matrix client capable of performing Firebase authentication and passing the following
@@ -19,35 +19,41 @@ If your client is Riot, you will need a custom version.
## Configuration ## Configuration
```yaml ```yaml
firebase.enabled: <boolean> firebase:
enabled: <boolean>
``` ```
Enable/disable this identity store. Enable/disable this identity store.
Example: Example:
```yaml ```yaml
firebase.enabled: <boolean> firebase:
enabled: <boolean>
``` ```
--- ---
```yaml ```yaml
firebase.credentials: <string> firebase:
credentials: <string>
``` ```
Path to the credentials file provided by Google Firebase to use with an external app. Path to the credentials file provided by Google Firebase to use with an external app.
Example: Example:
```yaml ```yaml
firebase.credentials: '/path/to/firebase/credentials.json' firebase:
credentials: '/path/to/firebase/credentials.json'
``` ```
--- ---
```yaml ```yaml
firebase.database: <string> firebase:
database: <string>
``` ```
URL to your Firebase database. URL to your Firebase database.
Example: Example:
```yaml ```yaml
firebase.database: 'https://my-project.firebaseio.com/' firebase:
database: 'https://my-project.firebaseio.com/'
``` ```

View File

@@ -8,40 +8,45 @@
For NetIQ, replace all the `ldap` prefix in the configuration by `netiq`. For NetIQ, replace all the `ldap` prefix in the configuration by `netiq`.
## Features ## Features
| Name | Supported? | | Name | Supported |
|----------------|------------| |-------------------------------------------------|-----------|
| Authentication | Yes | | [Authentication](../features/authentication.md) | Yes |
| Directory | Yes | | [Directory](../features/directory.md) | Yes |
| Identity | Yes | | [Identity](../features/identity.md) | Yes |
| Profile | Yes | | [Profile](../features/profile.md) | Yes |
## Getting started ## Getting started
### Base ### Base
To use your LDAP backend, add the bare minimum configuration in mxisd config file: To use your LDAP backend, add the bare minimum configuration in ma1sd config file:
```yaml ```yaml
ldap.enabled: true ldap:
ldap.connection.host: 'ldapHostnameOrIp' enabled: true
ldap.connection.port: 389 connection:
ldap.connection.bindDn: 'CN=My Mxisd User,OU=Users,DC=example,DC=org' host: 'ldapHostnameOrIp'
ldap.connection.bindPassword: 'TheUserPassword' port: 389
ldap.connection.baseDNs: bindDn: 'CN=My User,OU=Users,DC=example,DC=org'
bindPassword: 'TheUserPassword'
baseDNs:
- 'OU=Users,DC=example,DC=org' - 'OU=Users,DC=example,DC=org'
``` ```
These are standard LDAP connection configuration. mxisd will try to connect on port default port 389 without encryption. These are standard LDAP connection configuration. ma1sd will try to connect on port default port 389 without encryption.
If you would like to use several Base DNs, simply add more entries under `baseDNs`. If you would like to use several Base DNs, simply add more entries under `baseDNs`.
### TLS/SSL connection ### TLS/SSL connection
If you would like to use a TLS/SSL connection, use the following configuration options (STARTLS not supported): If you would like to use a TLS/SSL connection, use the following configuration options (STARTLS not supported):
```yaml ```yaml
ldap.connection.tls: true ldap:
ldap.connection.port: 12345 connection:
tls: true
port: 12345
``` ```
### Filter results ### Filter results
You can also set a default global filter on any LDAP queries: You can also set a default global filter on any LDAP queries:
```yaml ```yaml
ldap.filter: '(memberOf=CN=My Matrix Users,OU=Groups,DC=example,DC=org)' ldap:
filter: '(memberOf=CN=My Matrix Users,OU=Groups,DC=example,DC=org)'
``` ```
This example would only return users part of the group called `My Matrix Users`. This example would only return users part of the group called `My Matrix Users`.
This can be overwritten or append in each specific flow describe below. This can be overwritten or append in each specific flow describe below.
@@ -64,8 +69,11 @@ most certainly configure those mappings.
The following example would set the `sAMAccountName` attribute as a Matrix User ID localpart: The following example would set the `sAMAccountName` attribute as a Matrix User ID localpart:
```yaml ```yaml
ldap.attribute.uid.type: 'uid' ldap:
ldap.attribute.uid.value: 'sAMAccountName' attribute:
uid:
type: 'uid'
value: 'sAMAccountName'
``` ```
#### Display name #### Display name
@@ -73,20 +81,24 @@ Use `ldap.attribute.name`.
The following example would set the display name to the value of the `cn` attribute: The following example would set the display name to the value of the `cn` attribute:
```yaml ```yaml
ldap.attribute.name: 'cn' ldap:
attribute:
name: 'cn'
``` ```
#### 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/resources/application.yaml#L67) The following example would overwrite the [default list of attributes](../../src/main/java/io/kamax/ma1sd/config/ldap/LdapConfig.java#L64)
for emails and phone number: for emails and phone number:
```yaml ```yaml
ldap.attribute.threepid.email: ldap:
attribute:
threepid:
email:
- 'mail' - 'mail'
- 'otherMailAttribute' - 'otherMailAttribute'
msisdn:
ldap.attribute.threepid.msisdn:
- 'phone' - 'phone'
- 'otherPhoneAttribute' - 'otherPhoneAttribute'
``` ```
@@ -101,23 +113,28 @@ configuration item is needed to get started.
- `ldap.identity.medium`: Namespace to overwrite generated queries from the list of attributes for each 3PID medium. - `ldap.identity.medium`: Namespace to overwrite generated queries from the list of attributes for each 3PID medium.
### Authentication ### Authentication
No further configuration is needed to use the Authentication feature with LDAP once globally enabled and configured. After you have configured and enabled the [feature itself](../features/authentication.md), no further configuration is
needed with this identity store to make it work.
Profile auto-fill is enabled by default. It will use the `ldap.attribute.name` and `ldap.attribute.threepid` configuration Profile auto-fill is enabled by default. It will use the `ldap.attribute.name` and `ldap.attribute.threepid` configuration
options to get a lit of attributes to be used to build the user profile to pass on to synapse during authentication. options to get a lit of attributes to be used to build the user profile to pass on to synapse during authentication.
#### Configuration #### Configuration
- `ldap.auth.filter`: Specific user filter applied during identity search. Global filter is used if blank/not set. - `ldap.auth.filter`: Specific user filter applied during username search. Global filter is used if blank/not set.
### Directory ### Directory
No further configuration is needed to use the Directory feature with LDAP once globally enabled and configured. After you have configured and enabled the [feature itself](../features/directory.md), no further configuration is
needed with this identity store to make it work.
#### Configuration #### Configuration
To set a specific filter applied during directory search, use `ldap.directory.filter` To set a specific filter applied during directory search, use `ldap.directory.filter`
If you would like to use extra attributes in search that are not 3PIDs, like nicknames, group names, employee number: If you would like to use extra attributes in search that are not 3PIDs, like nicknames, group names, employee number:
```yaml ```yaml
ldap.directory.attribute.other: ldap:
directory:
attribute:
other:
- 'myNicknameAttribute' - 'myNicknameAttribute'
- 'memberOf' - 'memberOf'
- 'employeeNumberAttribute' - 'employeeNumberAttribute'

View File

@@ -20,13 +20,13 @@ To integrate this backend with your webapp, you will need to implement the REST
|--------------------------------------|------------------------------------------------|------------------------------------------------------| |--------------------------------------|------------------------------------------------|------------------------------------------------------|
| `rest.enabled` | `false` | Globally enable/disable the REST backend | | `rest.enabled` | `false` | Globally enable/disable the REST backend |
| `rest.host` | *None* | Default base URL to use for the different endpoints. | | `rest.host` | *None* | Default base URL to use for the different endpoints. |
| `rest.endpoints.auth` | `/_mxisd/backend/api/v1/auth/login` | Validate credentials and get user profile | | `rest.endpoints.auth` | `/_ma1sd/backend/api/v1/auth/login` | Validate credentials and get user profile |
| `rest.endpoints.directory` | `/_mxisd/backend/api/v1/directory/user/search` | Search for users by arbitrary input | | `rest.endpoints.directory` | `/_ma1sd/backend/api/v1/directory/user/search` | Search for users by arbitrary input |
| `rest.endpoints.identity.single` | `/_mxisd/backend/api/v1/identity/single` | Endpoint to query a single 3PID | | `rest.endpoints.identity.single` | `/_ma1sd/backend/api/v1/identity/single` | Endpoint to query a single 3PID |
| `rest.endpoints.identity.bulk` | `/_mxisd/backend/api/v1/identity/bulk` | Endpoint to query a list of 3PID | | `rest.endpoints.identity.bulk` | `/_ma1sd/backend/api/v1/identity/bulk` | Endpoint to query a list of 3PID |
| `rest.endpoints.profile.displayName` | `/_mxisd/backend/api/v1/profile/displayName` | Query the display name for a Matrix ID | `rest.endpoints.profile.displayName` | `/_ma1sd/backend/api/v1/profile/displayName` | Query the display name for a Matrix ID
| `rest.endpoints.profile.threepids` | `/_mxisd/backend/api/v1/profile/threepids` | Query the 3PIDs for a Matrix ID | `rest.endpoints.profile.threepids` | `/_ma1sd/backend/api/v1/profile/threepids` | Query the 3PIDs for a Matrix ID
| `rest.endpoints.profile.roles` | `/_mxisd/backend/api/v1/profile/roles` | Query the Roles for a Matrix ID | `rest.endpoints.profile.roles` | `/_ma1sd/backend/api/v1/profile/roles` | Query the Roles for a Matrix ID
Endpoint values can handle two formats: Endpoint values can handle two formats:
- URL Path starting with `/` that gets happened to the `rest.host` - URL Path starting with `/` that gets happened to the `rest.host`
@@ -268,3 +268,10 @@ Structure with example values:
} }
``` ```
The base `profile` key is mandatory. `display_name`, `threepids` and `roles` are only to be returned on the relevant request. The base `profile` key is mandatory. `display_name`, `threepids` and `roles` are only to be returned on the relevant request.
If there is no profile, the following response is expected:
```json
{
"profile": {}
}
```

View File

@@ -6,28 +6,30 @@
- SQLite - SQLite
## Features ## Features
| Name | Supported? | | Name | Supported |
|----------------|------------| |-------------------------------------------------|-----------|
| Authentication | No | | [Authentication](../features/authentication.md) | No |
| Directory | Yes | | [Directory](../features/directory.md) | Yes |
| Identity | Yes | | [Identity](../features/identity.md) | Yes |
| Profile | Yes | | [Profile](../features/profile.md) | Yes |
Due to the implementation complexity of supporting arbitrary hashing/encoding mechanisms or auth flow, Authentication Due to the implementation complexity of supporting arbitrary hashing/encoding mechanisms or auth flow, Authentication
will be out of scope of SQL Identity stores and should be done via one of the other identity stores, typically will be out of scope of SQL Identity stores and should be done via one of the other identity stores, typically
the [REST Identity store](rest.md). the [Exec Identity Store](exec.md) or the [REST Identity Store](rest.md).
## Configuration ## Configuration
### Basic ### Basic
```yaml ```yaml
sql.enabled: <boolean> sql:
enabled: <boolean>
``` ```
Enable/disable the identity store Enable/disable the identity store
--- ---
```yaml ```yaml
sql.type: <string> sql:
type: <string>
``` ```
Set the SQL backend to use: Set the SQL backend to use:
- `sqlite` - `sqlite`
@@ -38,14 +40,16 @@ Set the SQL backend to use:
### Connection ### Connection
#### SQLite #### SQLite
```yaml ```yaml
sql.connection: <string> sql:
connection: <string>
``` ```
Set the value to the absolute path to the Synapse SQLite DB file. Set the value to the absolute path to the Synapse SQLite DB file.
Example: `/path/to/sqlite/file.db` Example: `/path/to/sqlite/file.db`
#### Others #### Others
```yaml ```yaml
sql.connection: //<HOST[:PORT]/DB?user=USER&password=PASS sql:
connection: //<HOST[:PORT]/DB?user=USER&password=PASS
``` ```
Set the connection info for the database by replacing the following values: Set the connection info for the database by replacing the following values:
- `HOST`: Hostname of the SQL server - `HOST`: Hostname of the SQL server
@@ -58,14 +62,17 @@ This follow the JDBC URI syntax. See [official website](https://docs.oracle.com/
### Directory ### Directory
```yaml ```yaml
sql.directory.enabled: false sql:
directory:
enabled: false
``` ```
--- ---
```yaml ```yaml
sql.directory.query: sql:
directory:
query:
name: name:
type: <string> type: <string>
value: <string> value: <string>
@@ -73,7 +80,7 @@ sql.directory.query:
type: <string> type: <string>
value: <string> value: <string>
``` ```
For each query, `type` can be used to tell mxisd how to process the ID column: For each query, `type` can be used to tell ma1sd how to process the ID column:
- `localpart` will append the `matrix.domain` to it - `localpart` will append the `matrix.domain` to it
- `mxid` will use the ID as-is. If it is not a valid Matrix ID, the search will fail. - `mxid` will use the ID as-is. If it is not a valid Matrix ID, the search will fail.
@@ -83,7 +90,9 @@ For each query, `type` can be used to tell mxisd how to process the ID column:
Example: Example:
```yaml ```yaml
sql.directory.query: sql:
directory:
query:
name: name:
type: 'localpart' type: 'localpart'
value: 'SELECT idColumn, displayNameColumn FROM table WHERE displayNameColumn LIKE ?' value: 'SELECT idColumn, displayNameColumn FROM table WHERE displayNameColumn LIKE ?'
@@ -93,7 +102,41 @@ sql.directory.query:
``` ```
### Identity ### Identity
**NOTE**: Only single lookup is supported. Bulk lookup always returns no mapping. This is a restriction as the Matrix API
does not allow paging or otherwise limit of results of the API, potentially leading to thousands and thousands 3PIDs at once.
```yaml ```yaml
sql.identity.type: <string> sql:
sql.identity.query: <string> identity:
enabled: <boolean>
type: <string>
query: <string>
medium:
mediumTypeExample: <dedicated query>
``` ```
`type` is used to tell ma1sd how to process the returned `uid` column containing the User ID:
- `localpart` will build a full Matrix ID using the `matrix.domain` value.
- `mxid` will use the ID as-is. If it is not a valid Matrix ID, lookup(s) will fail.
A specific query can also given per 3PID medium type.
### Profile
```yaml
sql:
profile:
enabled: <boolean>
displayName:
query: <string>
threepid:
query: <string>
role:
type: <string>
query: <string>
```
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.
- `mxid` will use the ID as-is.
On each query, the first parameter `?` is set as a string with the corresponding ID format.

View File

@@ -1,27 +1,31 @@
# Synapse Identity Store # Synapse Identity Store
Synapse's Database itself can be used as an Identity store. Synapse's Database itself can be used as an Identity store. This identity store is a regular SQL store with
built-in default queries that matches Synapse DB.
## Features ## Features
| Name | Supported? | | Name | Supported |
|----------------|------------| |-------------------------------------------------|-----------|
| Authentication | No | | [Authentication](../features/authentication.md) | No |
| Directory | Yes | | [Directory](../features/directory.md) | Yes |
| Identity | Yes | | [Identity](../features/identity.md) | Yes |
| Profile | Yes | | [Profile](../features/profile.md) | Yes |
Authentication is done by Synapse itself. - Authentication is done by Synapse itself.
- Roles are mapped to communities. The Role name/ID uses the community ID in the form `+id:domain.tld`
## Configuration ## Configuration
### Basic ### Basic
```yaml ```yaml
synapseSql.enabled: <boolean> synapseSql:
enabled: <boolean>
``` ```
Enable/disable the identity store Enable/disable the identity store
--- ---
```yaml ```yaml
synapseSql.type: <string> synapseSql:
type: <string>
``` ```
Set the SQL backend to use which is configured in synapse: Set the SQL backend to use which is configured in synapse:
- `sqlite` - `sqlite`
@@ -29,14 +33,16 @@ Set the SQL backend to use which is configured in synapse:
### SQLite ### SQLite
```yaml ```yaml
synapseSql.connection: <string> synapseSql:
connection: <string>
``` ```
Set the value to the absolute path to the Synapse SQLite DB file. Set the value to the absolute path to the Synapse SQLite DB file.
Example: `/path/to/synapse/sqliteFile.db` Example: `/path/to/synapse/sqliteFile.db`
### PostgreSQL ### PostgreSQL
```yaml ```yaml
synapseSql.connection: //<HOST[:PORT]/DB?user=USER&password=PASS synapseSql:
connection: //<HOST[:PORT]/DB?user=USER&password=PASS
``` ```
Set the connection info for the database by replacing the following values: Set the connection info for the database by replacing the following values:
- `HOST`: Hostname of the SQL server - `HOST`: Hostname of the SQL server

View File

@@ -5,12 +5,12 @@ Two types of connections are required for full support:
- Direct SQL access - Direct SQL access
## Features ## Features
| Name | Supported? | | Name | Supported |
|----------------|------------| |-------------------------------------------------|-----------|
| Authentication | Yes | | [Authentication](../features/authentication.md) | Yes |
| Directory | Yes | | [Directory](../features/directory.md) | Yes |
| Identity | Yes | | [Identity](../features/identity.md) | Yes |
| Profile | No | | [Profile](../features/profile.md) | No |
## Requirements ## Requirements
- [Wordpress](https://wordpress.org/download/) >= 4.4 - [Wordpress](https://wordpress.org/download/) >= 4.4
@@ -29,27 +29,34 @@ define('JWT_AUTH_SECRET_KEY', 'your-top-secret-key');
#### Rewrite of `index.php` #### Rewrite of `index.php`
Wordpress is normally configured with rewrite of `index.php` so it does not appear in URLs. Wordpress is normally configured with rewrite of `index.php` so it does not appear in URLs.
If this is not the case for your installation, the mxisd URL will need to be appended with `/index.php` If this is not the case for your installation, the ma1sd URL will need to be appended with `/index.php`
### mxisd ### ma1sd
Enable in the configuration: Enable in the configuration:
```yaml ```yaml
wordpress.enabled: true wordpress:
enabled: true
``` ```
Configure the URL to your Wordpress installation - see above about added `/index.php`: Configure the URL to your Wordpress installation - see above about added `/index.php`:
```yaml ```yaml
wordpress.rest.base: 'http://localhost:8080' wordpress:
rest:
base: 'http://localhost:8080'
``` ```
Configure the SQL connection to your Wordpress database: Configure the SQL connection to your Wordpress database:
```yaml ```yaml
wordpress.sql.connection: '//127.0.0.1/wordpress?user=root&password=example' wordpress:
sql:
connection: '//127.0.0.1/wordpress?user=root&password=example'
``` ```
--- ---
By default, MySQL database is expected. If you use another database, use: By default, MySQL database is expected. If you use another database, use:
```yaml ```yaml
wordpress.sql.type: <string> wordpress:
sql:
type: <string>
``` ```
With possible values: With possible values:
- `mysql` - `mysql`
@@ -61,6 +68,8 @@ With possible values:
To configure the tables prefix for default queries, in case a custom value was set during Wordpress install: To configure the tables prefix for default queries, in case a custom value was set during Wordpress install:
```yaml ```yaml
wordpress.sql.tablePrefix: <string> wordpress:
sql:
tablePrefix: <string>
``` ```
By default, the value is set to `wp_`. By default, the value is set to `wp_`.

View File

@@ -1,19 +1,19 @@
# Email notifications - SMTP connector # Email notifications - SMTP connector
Enabled by default.
Connector ID: `smtp` Connector ID: `smtp`
## Configuration ## Configuration
```yaml ```yaml
threepid.medium.email: threepid:
medium:
email:
identity: identity:
from: 'identityServerEmail@example.org' from: 'identityServerEmail@example.org'
name: 'My Identity Server' name: 'My Identity Server'
connectors: connectors:
smtp: smtp:
host: 'smtpHostname' host: 'smtpHostname'
port: 587 tls: 1 # 0 = no STARTLS, 1 = try, 2 = force, 3 = TLS/SSL
tls: 1 # 0 = no STARTLS, 1 = try, 2 = force port: 587 # Set appropriate value depending on your TLS setting
login: 'smtpLogin' login: 'smtpLogin'
password: 'smtpPassword' password: 'smtpPassword'
``` ```

View File

@@ -1,11 +1,14 @@
# SMS notifications - Twilio connector # SMS notifications - Twilio connector
Enabled by default.
Connector ID: `twilio` Connector ID: `twilio`
## Configuration ## Configuration
```yaml ```yaml
threepid.medium.msisdn.connectors.twilio.accountSid: 'myAccountSid' threepid:
threepid.medium.msisdn.connectors.twilio.authToken: 'myAuthToken' medium:
threepid.medium.msisdn.connectors.twilio.number: '+123456789' msisdn:
connectors:
twilio:
account_sid: 'myAccountSid'
auth_token: 'myAuthToken'
number: '+123456789'
``` ```

View File

@@ -18,13 +18,16 @@ This handler can be used with the 3PID types:
## Configuration ## Configuration
Enabled by default or with: Enabled by default or with:
```yaml ```yaml
notification.handler.email: 'raw' notification:
handler:
email: 'raw'
``` ```
**WARNING:** Will be consolidated soon, prone to breaking changes. **WARNING:** Will be consolidated soon, prone to breaking changes.
Structure and default values: Structure and default values:
```yaml ```yaml
threepid.medium: threepid:
medium:
email: email:
identity: identity:
from: '' from: ''

View File

@@ -1,7 +1,39 @@
# SendGrid Notification handler # SendGrid Notification handler
To be completed. See [raw possible configuration items](https://github.com/kamax-matrix/mxisd/blob/master/src/main/resources/application.yaml#L172). > **WARNING:** This section is incomplete and may be misleading. Contact us if guidance is needed.
Enabled with: Enable with:
```yaml ```yaml
notification.handler.email: 'sendgrid' notification:
handler:
email: 'sendgrid'
```
Available Configuration keys:
```yaml
notification:
handlers:
sendgrid:
api:
key: <API key>
identity:
from: <Sender email address>
name: <Sender name>
templates:
invite:
subject: <Subject of the email notification sent for room invites>
body:
text: <Path to file containing the raw text part of the email. Do not set to not use one>
html: <Path to file containing the HTML part of the email. Do not set to not use one>
session:
validation:
subject: <Subject of the email notification sent for 3PID sessions>
body:
text: <Path to file containing the raw text part of the email. Do not set to not use one>
html: <Path to file containing the HTML part of the email. Do not set to not use one>
unbind:
notification:
subject: <Subject of the email notification sent for 3PID unbinds>
body:
text: <Path to file containing the raw text part of the email. Do not set to not use one>
html: <Path to file containing the raw text part of the email. Do not set to not use one>
``` ```

View File

@@ -1,73 +1,113 @@
# Notifications: Generate from templates # Notifications: Template generator
To create notification content, you can use the `template` generator if supported for the 3PID medium which will read Most of the Identity actions will trigger a notification of some kind, informing the user of some confirmation, next step
content from configured files. or just informing them about the current state of things.
Placeholders can be integrated into the templates to dynamically populate such content with relevant information like Those notifications are by default generated from templates and by replacing placeholder tokens in them with the relevant
the 3PID that was requested, the domain of your Identity server, etc. values of the notification. It is possible to customize the value of some placeholders, making easy to set values in the builtin templates, and/or
provide your own custom templates.
Templates can be configured for each event that would send a notification to the end user. Events share a set of common Templates for the following events/actions are available:
placeholders and also have their own individual set of placeholders. - [3PID invite](../../features/identity.md)
- [3PID session: validation](../session/session.md)
- [3PID session: unbind](https://github.com/kamax-matrix/ma1sd/wiki/ma1sd-and-your-privacy#improving-your-privacy-one-commit-at-the-time)
- [Matrix ID invite](../../features/experimental/application-service.md#email-notification-about-room-invites-by-matrix-ids)
## Placeholders
All placeholders **MUST** be surrounded with `%` in the template. Per example, the `DOMAIN` placeholder would become
`%DOMAIN%` within the template. This ensures replacement doesn't happen on non-placeholder strings.
### Global
The following placeholders are available in every template:
| Placeholder | Purpose |
|---------------------------------|------------------------------------------------------------------------------|
| `DOMAIN` | Identity server authoritative domain, as configured in `matrix.domain` |
| `DOMAIN_PRETTY` | Same as `DOMAIN` with the first letter upper case and all other lower case |
| `FROM_EMAIL` | Email address configured in `threepid.medium.<3PID medium>.identity.from` |
| `FROM_NAME` | Name configured in `threepid.medium.<3PID medium>.identity.name` |
| `RECIPIENT_MEDIUM` | The 3PID medium, like `email` or `msisdn` |
| `RECIPIENT_MEDIUM_URL_ENCODED` | URL encoded value of `RECIPIENT_MEDIUM` |
| `RECIPIENT_ADDRESS` | The address to which the notification is sent |
| `RECIPIENT_ADDRESS_URL_ENCODED` | URL encoded value of `RECIPIENT_ADDRESS` |
### Room invitation
Specific placeholders:
| Placeholder | Purpose |
|------------------------------|-----------------------------------------------------------------------------------|
| `SENDER_ID` | Matrix ID of the user who made the invite |
| `SENDER_NAME` | Display name of the user who made the invite, if not available/set, empty |
| `SENDER_NAME_OR_ID` | Display name of the user who made the invite. If not available/set, its Matrix ID |
| `INVITE_MEDIUM` | The 3PID medium for the invite. |
| `INVITE_MEDIUM_URL_ENCODED` | URL encoded value of `INVITE_MEDIUM` |
| `INVITE_ADDRESS` | The 3PID address for the invite. |
| `INVITE_ADDRESS_URL_ENCODED` | URL encoded value of `INVITE_ADDRESS` |
| `ROOM_ID` | The Matrix ID of the Room in which the invite took place |
| `ROOM_NAME` | The Name of the room in which the invite took place. If not available/set, empty |
| `ROOM_NAME_OR_ID` | The Name of the room in which the invite took place. If not available/set, its ID |
| `REGISTER_URL` | The URL to provide to the user allowing them to register their account, if needed |
### Validation of 3PID Session
Specific placeholders:
| Placeholder | Purpose |
|--------------------|--------------------------------------------------------------------------------------|
| `VALIDATION_LINK` | URL, including token, to validate the 3PID session. |
| `VALIDATION_TOKEN` | The token needed to validate the session, in case the user cannot use the link. |
| `NEXT_URL` | URL to redirect to after the sessions has been validated. |
## Templates
ma1sd comes with a set of builtin templates to easily get started. Those templates can be found
[in the repository](https://github.com/ma1uta/ma1sd/tree/master/src/main/resources/threepids). If you want to use
customized templates, we recommend using the builtin templates as a starting point.
> **NOTE**: The link above point to the latest version of the built-in templates. Those might be different from your
version. Be sure to view the repo at the current tag.
## Configuration ## Configuration
All configuration is specific to [3PID mediums](https://matrix.org/docs/spec/appendices.html#pid-types) and happen
under the namespace `threepid.medium.<medium>.generators.template`.
Under such namespace, the following keys are available:
- `invite`: Path to the 3PID invite notification template
- `session.validation`: Path to the 3PID session validation notification template
- `session.unbind`: Path to the 3PID session unbind notification template
- `generic.matrixId`: Path to the Matrix ID invite notification template
- `placeholder`: Map of key/values to set static values for some placeholders.
The `placeholder` map supports the following keys, mapped to their respective template placeholders:
- `REGISTER_URL`
### Example
#### Simple
```yaml
threepid:
medium:
email:
generators:
template:
placeholder:
REGISTER_URL: 'https://matrix-client.example.org'
```
In this configuration, the builtin templates are used and a static value for the `REGISTER_URL` is set, allowing to point
a newly invited user to a webapp allowing the creation of its account on the server.
#### Advanced
To configure paths to the various templates: To configure paths to the various templates:
```yaml ```yaml
threepid.medium.<YOUR 3PID MEDIUM HERE>: threepid:
medium:
email:
generators: generators:
template: template:
invite: '/path/to/invite-template.eml' invite: '/path/to/invite-template.eml'
session: session:
validation: validation: '/path/to/validate-template.eml'
local: '/path/to/validate-local-template.eml' unbind:
remote: 'path/to/validate-remote-template.eml' notification: '/path/to/unbind-notification-template.eml'
generic: generic:
matrixId: '/path/to/mxid-invite-template.eml' matrixId: '/path/to/mxid-invite-template.eml'
placeholder:
REGISTER_URL: 'https://matrix-client.example.org'
``` ```
The `template` generator is usually the default, so no further configuration is needed. In this configuration, a custom template is used for each event and a static value for the `REGISTER_URL` is set.
## Global placeholders
| Placeholder | Purpose |
|-----------------------|------------------------------------------------------------------------------|
| `%DOMAIN%` | Identity server authoritative domain, as configured in `matrix.domain` |
| `%DOMAIN_PRETTY%` | Same as `%DOMAIN%` with the first letter upper case and all other lower case |
| `%FROM_EMAIL%` | Email address configured in `threepid.medium.<3PID medium>.identity.from` |
| `%FROM_NAME%` | Name configured in `threepid.medium.<3PID medium>.identity.name` |
| `%RECIPIENT_MEDIUM%` | The 3PID medium, like `email` or `msisdn` |
| `%RECIPIENT_ADDRESS%` | The address to which the notification is sent |
## Events
### Room invitation
This template is used when someone is invited into a room using an email address which has no known bind to a Matrix ID.
#### Placeholders
| Placeholder | Purpose |
|-----------------------|------------------------------------------------------------------------------------------|
| `%SENDER_ID%` | Matrix ID of the user who made the invite |
| `%SENDER_NAME%` | Display name of the user who made the invite, if not available/set, empty |
| `%SENDER_NAME_OR_ID%` | Display name of the user who made the invite. If not available/set, its Matrix ID |
| `%INVITE_MEDIUM%` | The 3PID medium for the invite. |
| `%INVITE_ADDRESS%` | The 3PID address for the invite. |
| `%ROOM_ID%` | The Matrix ID of the Room in which the invite took place |
| `%ROOM_NAME%` | The Name of the room in which the invite took place. If not available/set, empty |
| `%ROOM_NAME_OR_ID%` | The Name of the room in which the invite took place. If not available/set, its Matrix ID |
### Local validation of 3PID Session
This template is used when to user which added their 3PID address to their profile/settings and the session policy
allows at least local sessions.
#### Placeholders
| Placeholder | Purpose |
|----------------------|--------------------------------------------------------------------------------------|
| `%VALIDATION_LINK%` | URL, including token, to validate the 3PID session. |
| `%VALIDATION_TOKEN%` | The token needed to validate the local session, in case the user cannot use the link |
### Remote validation of 3PID Session
This template is used when to user which added their 3PID address to their profile/settings and the session policy only
allows remote sessions.
**NOTE:** 3PID session always require local validation of a token, even if a remote session is enforced.
One cannot bind a Matrix ID to the session until both local and remote sessions have been validated.
#### Placeholders
| Placeholder | Purpose |
|----------------------|--------------------------------------------------------|
| `%VALIDATION_TOKEN%` | The token needed to validate the session |
| `%NEXT_URL%` | URL to continue with remote validation of the session. |

View File

@@ -1,86 +1,42 @@
# Web pages for the 3PID sessions # Web pages for the 3PID sessions
You can customize the various pages used during a 3PID validation using [Thymeleaf templates](http://www.thymeleaf.org/). You can customize the various pages used during a 3PID validation using the options below.
## Configuration ## Configuration
Pseudo-configuration to illustrate the structure: Pseudo-configuration to illustrate the structure:
```yaml ```yaml
# CONFIGURATION EXAMPLE # CONFIGURATION EXAMPLE
# DO NOT COPY/PASTE THIS IN YOUR CONFIGURATION # DO NOT COPY/PASTE THIS IN YOUR CONFIGURATION
view.session.local: view:
session:
onTokenSubmit: onTokenSubmit:
success: '/path/to/session/local/tokenSubmitSuccess-page.html' success: '/path/to/session/tokenSubmitSuccess-page.html'
failure: '/path/to/session/local/tokenSubmitFailure-page.html' failure: '/path/to/session/tokenSubmitFailure-page.html'
view.session.localRemote:
onTokenSubmit:
success: '/path/to/session/localRemote/tokenSubmitSuccess-page.html'
failure: '/path/to/session/local/tokenSubmitFailure-page.html'
view.session.remote:
onRequest:
success: '/path/to/session/remote/requestSuccess-page.html'
failure: '/path/to/session/remote/requestFailure-page.html'
onCheck:
success: '/path/to/session/remote/checkSuccess-page.html'
failure: '/path/to/session/remote/checkFailure-page.html'
# CONFIGURATION EXAMPLE # CONFIGURATION EXAMPLE
# DO NOT COPY/PASTE THIS IN YOUR CONFIGURATION # DO NOT COPY/PASTE THIS IN YOUR CONFIGURATION
``` ```
3PID session are divided into three config sections: `view.session`:
- `local` for local-only 3PID sessions
- `localRemote` for local 3PID sessions that can also be turned into remote sessions, if the user so desires
- `remote` for remote-only 3PID sessions
Each section contains a sub-key per support event. Finally, a `success` and `failure` key is available depending on the
outcome of the request.
## Local
### onTokenSubmit
This is triggered when a user submit a validation token for a 3PID session. It is typically visited when clicking the This is triggered when a user submit a validation token for a 3PID session. It is typically visited when clicking the
link in a validation email. link in a validation email.
The template should typically inform the user that the validation was successful and to go back in their Matrix client The template should typically inform the user that the validation was successful and to go back in their Matrix client
to finish the validation process. to finish the validation process, or that the validation failed.
#### Placeholders Two configuration keys are available that accept paths to HTML templates:
- `success`
- `failure`
### Serving static assets
ma1sd will not serve any static asset (images, JS, CSS, etc.). If such are needed, you will need to serve them using the
reverse proxy sitting in front of ma1sd using a path outside of the `/_matrix/identity/` namespace. We advise using
the base path `/static/` for such use cases, allowing to remain under the same hostname/origin.
You can also serve such assets using absolute URL, possibly under other domains, but be aware of Cross-Origin restrictions
in browsers which are out of scope of ma1sd.
## Placeholders
### Success
No object/placeholder are currently available. No object/placeholder are currently available.
## Local & Remote ### Failure
### onTokenSubmit
This is triggered when a user submit a validation token for a 3PID session. It is typically visited when clicking the
link in a validation email.
The template should typically inform the user that their 3PID address will not yet be publicly/globally usable. In case
they want to make it, they should start a Remote 3PID session with a given link or that they can go back to their Matrix
client if they do not wish to proceed any further.
#### Placeholders
##### Success
`<a th:href="${remoteSessionLink}">text</a>` can be used to display the link to start a Remote 3PID session.
##### Failure
No object/placeholder are currently available.
## Remote
### onRequest
This is triggered when a user starts a Remote 3PID session, usually from a link produced in the `local.onTokenSubmit`
view or in a remote-only 3PID notification.
The template should typically inform the user that the remote creation was successful, followed the instructions sent by
the remote Identity server and, once that is done, click a link to validate the session.
#### Placeholders
##### Success
`<a th:href="${checkLink}">text</a>` can be used to display the link to validate the Remote 3PID session.
##### Failure
No object/placeholder are currently available.
### onCheck
This is triggered when a user attempts to inform the Identity server that the Remote 3PID session has been validated
with the remote Identity server.
The template should typically inform the user that the validation was successful and to go back in their Matrix client
to finish the validation process.
#### Placeholders
No object/placeholder are currently available. No object/placeholder are currently available.

View File

@@ -1,9 +1,8 @@
# 3PID Sessions # 3PID Sessions
- [Overview](#overview) - [Overview](#overview)
- [Purpose](#purpose) - [Restrictions](#restrictions)
- [Federation](#federation) - [Bindings](#bindings)
- [3PID scope](#3pid-scope) - [Federation](#federation)
- [Session scope](#session-scope)
- [Notifications](#notifications) - [Notifications](#notifications)
- [Email](#email) - [Email](#email)
- [Phone numbers](#msisdn-(phone-numbers)) - [Phone numbers](#msisdn-(phone-numbers))
@@ -11,28 +10,39 @@
- [Configuration](#configuration) - [Configuration](#configuration)
- [Web views](#web-views) - [Web views](#web-views)
- [Scenarios](#scenarios) - [Scenarios](#scenarios)
- [Default](#default)
- [Local sessions only](#local-sessions-only)
- [Remote sessions only](#remote-sessions-only)
- [Sessions disabled](#sessions-disabled) - [Sessions disabled](#sessions-disabled)
## Overview ## Overview
When adding an email, a phone number or any other kind of 3PID (Third-Party Identifier) in a Matrix client, When adding an email, a phone number or any other kind of 3PID (Third-Party Identifier) in a Matrix client,
the identity server is contacted to validate the 3PID. the identity server is contacted to validate the 3PID.
To validate the 3PID the identity server sends a message to the 3PID (e.g. an To validate the 3PID, the identity server creates a session associated with a secret token. That token is sent via a message
email) with a hyperlink back to a web-page managed by the identity server to to the 3PID (e.g. an email) with a the necessary info so the user can submit them to the Identity Server, confirm ownership
confirm ownership of the 3PID. of the 3PID.
Once this 3PID is validated, the Homeserver will publish the user Matrix ID on the Identity Server and Once this 3PID is validated, the Homeserver will request that the Identity Server links the provided user Matrix ID with
add this 3PID to the Matrix account which initiated the request. the 3PID session and finally add the 3PID to its own data store.
This serves two purposes: This serves two purposes:
- Add the 3PID as an administrative/login info for the Homeserver directly - Add the 3PID as an administrative/login info for the Homeserver directly
- Publish, or *Bind*, the 3PID so it can be queried from Homeservers and clients when inviting someone in a room - Links, called *Bind*, the 3PID so it can be queried from Homeservers and clients when inviting someone in a room
by a 3PID, allowing it to be resolved to a Matrix ID. by a 3PID, allowing it to be resolved to a Matrix ID.
## Federation ## Restrictions
### Bindings
ma1sd does not store bindings directly. While a user can see its email, phone number or any other 3PID in its
settings/profile, it does **NOT** mean it is published/saved anywhere or can be used to invite/search the user.
Identity stores are the ones holding such data, irrelevant if a user added a 3PID to their profile. When queried for
bindings, ma1sd will query Identity stores which are responsible to store this kind of information.
Therefore, by default, any 3PID added to a user profile which is NOT within a configured and enabled Identity backend
will simply not be usable for search or invites, **even on the same Homeserver!**
To have such 3PID bindings available for search and invite queries on synapse, use its dedicated
[Identity store](../../stores/synapse.md).
### Federation
In a federated set up, identity servers must cooperate to find the Matrix ID associated with a 3PID. In a federated set up, identity servers must cooperate to find the Matrix ID associated with a 3PID.
Federation is based on the principle that each server is responsible for its own (dns) domain. Federation is based on the principle that each server is responsible for its own (dns) domain.
@@ -43,68 +53,22 @@ Example: a user from Homeserver `example.org` adds an email `john@example.com`.
Federated identity servers would try to find the identity server at `example.com` and ask it for the Matrix ID of associated with `john@example.com`. Federated identity servers would try to find the identity server at `example.com` and ask it for the Matrix ID of associated with `john@example.com`.
Nevertheless, Matrix users might add 3PIDs that are not associated to a domain, for example telephone numbers. Nevertheless, Matrix users might add 3PIDs that are not associated to a domain, for example telephone numbers.
Or they might even add 3PIDs associated to a different domain (such as an email address hosted by gmail). Or they might even add 3PIDs associated to a different domain (such as an email address hosted by Gmail).
Such 3PIDs cannot be resolved in a federated way. Such 3PIDs cannot be resolved in a federated way and will not be found from other servers.
Example: a user from Homeserver `example.org` adds an email `john@gmail.com`. Example: a user from Homeserver `example.org` adds an email `john@gmail.com`.
If a federated lookup was performed, Identity servers would try to find the 3PID bind at the `gmail.com` server, and If a federated lookup was performed, Identity servers would try to find the 3PID bind at the `gmail.com` server, and
not `example.org`. not `example.org`.
In order to resolve such 3PIDs, i.e. 3PIDs that cannot be resolved in a Federated way, an identity server can be configured such that As ma1sd is built for self-hosted use cases, mainly for orgs/corps, this is usually not a problem for emails.
- 3PIDs that cannot be resolved locally or using federation, are fowarded to another global identity server. Sadly, there is currently no mechanism to make this work for phone numbers.
- registration of new 3PIDs that cannot be looked up in a federated fashion, is forwarded to another global identity server.
By forwarding a 3PIDs registration the identity creates a *Remote session* and *Remote bind*, effectively starting a new 3PID session with another Identity server on
behalf of the user.
To ensure lookup works consistency within the current Matrix network, the central Matrix.org Identity Server should be
used to store *remote* sessions and binds.
However, at the time of writing, the Matrix specification and the central Matrix.org servers do not allow to remote a 3PID bind.
This means that once a 3PID is published (email, phone number, etc.), it cannot be easily removed
and would require contacting the Matrix.org administrators for each bind individually.
This poses a privacy, control and security concern, especially for groups/corporations that want to keep a tight control
on where such identifiers can be made publicly visible.
To ensure full control, validation management relies on two concepts:
- The scope of 3PID being validated
- The scope of 3PID sessions that should be possible/offered
### 3PID scope
3PID can either be scoped as local or remote.
Local means that they can be looked up using federation and that such a federation call would end up on the local
Identity Server.
Remote means that they cannot be lookup using federation or that a federation call would not end up on the local
Identity Server.
Email addresses can either be local or remote 3PID, depending on the domain. If the address is one from the configured
domain in the Identity server, it will be scoped as local. If it is from another domain, it will be as remote.
Phone number can only be scoped as remote, since there is currently no way to perform DNS queries that would lead back
to the Identity server who validated the phone number.
### Session scope
Sessions can be scoped as:
- Local only - validate 3PIDs directly, do not allow the creation of 3PID sessions on a remote Identity server.
- Local and Remote - validate 3PIDs directly, offer users to option to also validate and bind 3PID on another server.
- Remote only - validate and bind 3PIDs on another server, no validation or bind done locally.
---
**IMPORTANT NOTE:** mxisd does not store bindings directly. While a user can see its email, phone number or any other
3PID in its settings/profile, it does **NOT** mean it is published anywhere and can be used to invite/search the user.
Identity stores are the ones holding such data.
If you still want added arbitrary 3PIDs to be discoverable on a synapse Homeserver, use the corresponding [Identity store](../../stores/synapse.md).
See the [Scenarios](#scenarios) for more info on how and why.
## Notifications ## Notifications
3PIDs are validated by sending a pre-formatted message containing a token to that 3PID address, which must be given to the 3PIDs are validated by sending a pre-formatted message containing a token to that 3PID address, which must be given to the
Identity server that received the request. This is usually done by means of a URL to visit for email or a short number Identity server that received the request. This is usually done by means of a URL to visit for email or a short number
received by SMS for phone numbers. received by SMS for phone numbers.
mxisd use two components for this: ma1sd use two components for this:
- Generator which produces the message to be sent with the necessary information the user needs to validate their session. - Generator which produces the message to be sent with the necessary information the user needs to validate their session.
- Connector which actually send the notification (e.g. SMTP for email). - Connector which actually send the notification (e.g. SMTP for email).
@@ -126,44 +90,32 @@ Connectors:
## Usage ## Usage
### Configuration ### Configuration
The following example of configuration (incomplete extract) shows which items are relevant for 3PID sessions. The following example of configuration shows which items are relevant for 3PID sessions.
**IMPORTANT:** Most configuration items shown have default values and should not be included in your own configuration **IMPORTANT:** Most configuration items shown have default values and should not be included in your own configuration
file unless you want to specifically overwrite them. file unless you want to specifically overwrite them.
```yaml ```yaml
# CONFIGURATION EXAMPLE # CONFIGURATION EXAMPLE
# DO NOT COPY/PASTE THIS IN YOUR CONFIGURATION # DO NOT COPY/PASTE AS-IS IN YOUR CONFIGURATION
session.policy.validation.enabled: true
session.policy.validation.forLocal: session:
policy:
validation:
enabled: true enabled: true
toLocal: true unbind:
toRemote: notification:
enabled: true enabled: true
server: 'configExample' # Not to be included in config! Already present in default config!
session.policy.validation.forRemote: # DO NOT COPY/PASTE AS-IS IN YOUR CONFIGURATION
enabled: true
toLocal: true
toRemote:
enabled: true
server: 'configExample' # Not to be included in config! Already present in default config!
# DO NOT COPY/PASTE THIS IN YOUR CONFIGURATION
# CONFIGURATION EXAMPLE # CONFIGURATION EXAMPLE
``` ```
`session.policy.validation` is the core configuration to control what users configured to use your Identity server `session.policy.validation` is the core configuration to control what users configured to use your Identity server
are allowed to do in terms of 3PID sessions. are allowed to do in terms of 3PID sessions. The policy has a global on/off switch for 3PID sessions using `.enabled`
The policy has a global on/off switch for 3PID sessions using `.enabled` ---
It is also divided into two sections: `forLocal` and `forRemote` which refers to the 3PID scopes.
Each scope is divided into three parts: `unbind` controls warning notifications for 3PID removal.
- global on/off switch for 3PID sessions using `.enabled`
- `toLocal` allowing or not local 3PID session validations
- `toRemote` allowing or not remote 3PID session validations and to which server such sessions should be sent.
`.server` takes a Matrix Identity server list label. Only the first server in the list is currently used.
If both `toLocal` and `toRemote` are enabled, the user will be offered to initiate a remote session once their 3PID
locally validated.
### Web views ### Web views
Once a user click on a validation link, it is taken to the Identity Server validation page where the token is submitted. Once a user click on a validation link, it is taken to the Identity Server validation page where the token is submitted.
@@ -174,103 +126,18 @@ See [the dedicated document](session-views.md)
on how to configure/customize/brand those pages to your liking. on how to configure/customize/brand those pages to your liking.
### Scenarios ### Scenarios
It is important to keep in mind that mxisd does not create bindings, irrelevant if a user added a 3PID to their profile.
Instead, when queried for bindings, mxisd will query Identity stores which are responsible to store this kind of information.
This has the side effect that any 3PID added to a user profile which is NOT within a configured and enabled Identity backend
will simply not be usable for search or invites, **even on the same Homeserver!**
mxisd does not store binds on purpose, as one of its primary goal is to ensure maximum compatibility with federation
and the rest of the Matrix ecosystem is preserved.
Nonetheless, because mxisd also aims at offering support for tight control over identity data, it is possible to have
such 3PID bindings available for search and invite queries on synapse with the corresponding [Identity store](../../stores/synapse.md).
See the [Local sessions only](#local-sessions-only) use case for more information on how to configure.
#### Default
By default, mxisd allows the following:
| | Local Session | Remote Session |
|-----------------|-------------------|----------------|
| **Local 3PID** | Yes | Yes, offered |
| **Remote 3PID** | No, Remote forced | Yes |
This is usually what people expect and will feel natural to users and does not involve further integration.
This allows to stay in control for e-mail addresses which domain matches your Matrix environment, still making them
discoverable with federation but not recorded in a 3rd party Identity server which is not under your control.
Users still get the possibility to publish globally their address if needed.
Other e-mail addresses and phone number will be redirected to remote sessions to ensure full compatibility with the Matrix
ecosystem and other federated servers.
#### Local sessions only
**NOTE:** This does not affect 3PID lookups (queries to find Matrix IDs). See [Federation](../../features/federation.md)
to disable remote lookup for those.
This configuration ensures maximum confidentiality and privacy.
Typical use cases:
- Private Homeserver, not federated
- Internal Homeserver without direct Internet access
- Custom product based on Matrix which does not federate
No 3PID will be sent to a remote Identity server and all validation will be performed locally.
On the flip side, people with *Remote* 3PID scopes will not be found from other servers.
Use the following values:
```yaml
session.policy.validation.enabled: true
session.policy.validation.forLocal:
enabled: true
toLocal: true
toRemote:
enabled: false
session.policy.validation.forRemote:
enabled: true
toLocal: true
toRemote:
enabled: false
```
**IMPORTANT**: When using local-only mode and if you are using synapse, you will also need to enable its dedicated Identity
store if you want user searches and invites to work. To do so, see the [dedicated document](../../stores/synapse.md).
#### Remote sessions only
This configuration ensures all 3PID are made public for maximum compatibility and reach within the Matrix ecosystem, at
the cost of confidentiality and privacy.
Typical use cases:
- Public Homeserver
- Homeserver with registration enabled
Use the following values:
```yaml
session.policy.validation.enabled: true
session.policy.validation.forLocal:
enabled: true
toLocal: false
toRemote:
enabled: true
session.policy.validation.forRemote:
enabled: true
toLocal: false
toRemote:
enabled: true
```
#### Sessions disabled #### Sessions disabled
This configuration would disable 3PID session altogether, preventing users from adding emails and/or phone numbers to This configuration would disable 3PID sessions altogether, preventing users from validating emails and/or phone numbers
their profiles. and any subsequent actions that requires them, like adding them to their profiles.
This would be used if mxisd is also performing authentication for the Homeserver, typically with synapse and the
[REST password provider](https://github.com/kamax-io/matrix-synapse-rest-auth).
**This mode comes with several important restrictions:** This would be used if ma1sd is also performing authentication for the Homeserver, typically with synapse and the
- This does not prevent users from removing 3PID from their profile. They would be unable to add them back! [REST password provider](https://github.com/ma1uta/matrix-synapse-rest-password-provider), where 3PID mappings would be
- This prevents users from initiating remote session to make their 3PID binds globally visible auto-populated.
It is therefore recommended to not fully disable sessions but instead restrict specific set of 3PID and Session scopes.
Use the following values to enable this mode: Use the following values to enable this mode:
```yaml ```yaml
session.policy.validation.enabled: false session:
policy:
validation:
enabled: false
``` ```

58
docs/troubleshooting.md Normal file
View File

@@ -0,0 +1,58 @@
# Troubleshooting
- [Purpose](#purpose)
- [Logs](#logs)
- [Locations](#locations)
- [Reading Them](#reading-them)
- [Common issues](#common-issues)
- [Submit an issue](#submit-an-issue)
## Purpose
This document describes basic troubleshooting steps for ma1sd.
## Logs
### Locations
ma1sd logs to `STDOUT` (Standard Output) and `STDERR` (Standard Error) only, which gets redirected
to log file(s) depending on your system.
If you use the [Debian package](install/debian.md), this goes to `syslog`.
If you use the [Docker image](install/docker.md), this goes to the container logs.
For any other platform, please refer to your package maintainer.
### Increase verbosity
To increase log verbosity and better track issues, the following means are available:
- Add the `-v` command line parameter
- Use the environment variable and value `MA1SD_LOG_LEVEL=debug`
### Reading them
Before reporting an issue, it is important to produce clean and complete logs so they can be understood.
It is usually useless to try to troubleshoot an issue based on a single log line. Any action or API request
in ma1sd would trigger more than one log lines, and those would be considered necessary context to
understand what happened.
You may also find things called *stacktraces*. Those are important to pin-point bugs and the likes and should
always be included in any report. They also tend to be very specific about the issue at hand.
Example of a stacktrace:
```
Exception in thread "main" java.lang.NullPointerException
at com.example.myproject.Book.getTitle(Book.java:16)
at com.example.myproject.Author.getBookTitles(Author.java:25)
at com.example.myproject.Bootstrap.main(Bootstrap.java:14)
```
### Common issues
#### Internal Server Error
`Contact your administrator with reference Transaction #123456789`
This is a generic message produced in case of an unknown error. The transaction reference allows to easily find
the location in the logs to look for an error.
**IMPORTANT:** That line alone does not tell you anything about the error. You'll need the log lines before and after,
usually including a stacktrace, to know what happened. Please take the time to read the surround output to get
context about the issue at hand.
## Submit an issue
In case the logs do not allow you to understand the issue at hand, please submit clean and complete logs
as explained [here](#reading-them) in a new issue on the repository, or [get in touch](../README.md#contact).

Binary file not shown.

View File

@@ -1,6 +1,6 @@
#Fri Aug 11 17:19:02 CEST 2017 #Thu Jul 04 22:47:59 MSK 2019
distributionUrl=https\://services.gradle.org/distributions/gradle-5.5.1-all.zip
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.0.2-bin.zip zipStoreBase=GRADLE_USER_HOME

18
gradlew vendored
View File

@@ -1,5 +1,21 @@
#!/usr/bin/env sh #!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
############################################################################## ##############################################################################
## ##
## Gradle start up script for UN*X ## Gradle start up script for UN*X
@@ -28,7 +44,7 @@ APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"` APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS="" DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum" MAX_FD="maximum"

18
gradlew.bat vendored
View File

@@ -1,3 +1,19 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem http://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off @if "%DEBUG%" == "" @echo off
@rem ########################################################################## @rem ##########################################################################
@rem @rem
@@ -14,7 +30,7 @@ set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME% set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS= set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe @rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome if defined JAVA_HOME goto findJavaFromJavaHome

111
ma1sd.example.yaml Normal file
View File

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

View File

@@ -1,7 +1,9 @@
Package: mxisd Package: ma1sd
Maintainer: Kamax.io <foss@kamax.io> Maintainer: ma1uta <sablintolya@gmail.com>
Homepage: https://github.com/kamax-matrix/mxisd Homepage: https://github.com/ma1uta/ma1sd
Description: Federated Matrix Identity Server Description: Federated Matrix Identity Server
Architecture: all Architecture: all
Depends: openjdk-8-jre | openjdk-8-jre-headless | openjdk-8-jdk | openjdk-8-jdk-headless Section: net
Priority: optional
Depends: openjdk-8-jre | openjdk-8-jre-headless | openjdk-8-jdk | openjdk-8-jdk-headless | openjdk-11-jre | openjdk-11-jre-headless | openjdk-11-jdk | openjdk-11-jdk-headless
Version: 0 Version: 0

View File

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

View File

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

View File

@@ -1,25 +1,34 @@
#!/usr/bin/env bash #!/bin/bash
if [[ -n "$CONF_FILE_PATH" ]] && [ ! -f "$CONF_FILE_PATH" ]; then if [[ -n "$CONF_FILE_PATH" ]] && [ ! -f "$CONF_FILE_PATH" ]; then
echo "Generating config file $CONF_FILE_PATH" echo "Generating config file $CONF_FILE_PATH"
touch "CONF_FILE_PATH" touch "CONF_FILE_PATH"
if [[ -n "$MATRIX_DOMAIN" ]]; then if [[ -n "$MATRIX_DOMAIN" ]]; then
echo "Setting matrix domain to $MATRIX_DOMAIN" echo "Setting matrix domain to $MATRIX_DOMAIN"
echo "matrix.domain: $MATRIX_DOMAIN" >> "$CONF_FILE_PATH" echo "matrix:" >> "$CONF_FILE_PATH"
echo " domain: '$MATRIX_DOMAIN'" >> "$CONF_FILE_PATH"
echo >> "$CONF_FILE_PATH"
fi fi
if [[ -n "$SIGN_KEY_PATH" ]]; then if [[ -n "$SIGN_KEY_PATH" ]]; then
echo "Setting signing key path to $SIGN_KEY_PATH" echo "Setting signing key path to $SIGN_KEY_PATH"
echo "key.path: $SIGN_KEY_PATH" >> "$CONF_FILE_PATH" echo "key:" >> "$CONF_FILE_PATH"
echo " path: '$SIGN_KEY_PATH'" >> "$CONF_FILE_PATH"
echo >> "$CONF_FILE_PATH"
fi fi
if [[ -n "$SQLITE_DATABASE_PATH" ]]; then if [[ -n "$SQLITE_DATABASE_PATH" ]]; then
echo "Setting SQLite DB path to $SQLITE_DATABASE_PATH" echo "Setting SQLite DB path to $SQLITE_DATABASE_PATH"
echo "storage.provider.sqlite.database: $SQLITE_DATABASE_PATH" >> "$CONF_FILE_PATH" echo "storage:" >> "$CONF_FILE_PATH"
echo " provider:" >> "$CONF_FILE_PATH"
echo " sqlite:" >> "$CONF_FILE_PATH"
echo " database: '$SQLITE_DATABASE_PATH'" >> "$CONF_FILE_PATH"
echo >> "$CONF_FILE_PATH"
fi fi
echo "Starting mxisd..." echo "Starting ma1sd..."
echo echo
fi fi
exec java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -Dspring.config.location=/etc/mxisd/ -Dspring.config.name=mxisd -jar /mxisd.jar exec java -jar /app/ma1sd.jar -c /etc/ma1sd/ma1sd.yaml

View File

@@ -1,27 +1,26 @@
/* /*
* The MIT License * The MIT License
* *
* Copyright (c) 2013 Edin Dazdarevic (edin.dazdarevic@gmail.com) * Copyright (c) 2013 Edin Dazdarevic (edin.dazdarevic@gmail.com)
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights * in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is * copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions: * furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in * The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software. * all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
* */
* */
package edazdarevic.commons.net; package edazdarevic.commons.net;
@@ -37,6 +36,7 @@ import java.util.List;
* both IPv4 and IPv6. * both IPv4 and IPv6.
*/ */
public class CIDRUtils { public class CIDRUtils {
private final String cidr; private final String cidr;
private InetAddress inetAddress; private InetAddress inetAddress;
@@ -44,7 +44,6 @@ public class CIDRUtils {
private InetAddress endAddress; private InetAddress endAddress;
private final int prefixLength; private final int prefixLength;
public CIDRUtils(String cidr) throws UnknownHostException { public CIDRUtils(String cidr) throws UnknownHostException {
this.cidr = cidr; this.cidr = cidr;
@@ -66,7 +65,6 @@ public class CIDRUtils {
private void calculate() throws UnknownHostException { private void calculate() throws UnknownHostException {
ByteBuffer maskBuffer; ByteBuffer maskBuffer;
int targetSize; int targetSize;
if (inetAddress.getAddress().length == 4) { if (inetAddress.getAddress().length == 4) {
@@ -120,14 +118,9 @@ public class CIDRUtils {
} }
public String getNetworkAddress() { public String getNetworkAddress() {
return this.startAddress.getHostAddress(); return this.startAddress.getHostAddress();
} }
public String getBroadcastAddress() {
return this.endAddress.getHostAddress();
}
public boolean isInRange(String ipAddress) throws UnknownHostException { public boolean isInRange(String ipAddress) throws UnknownHostException {
InetAddress address = InetAddress.getByName(ipAddress); InetAddress address = InetAddress.getByName(ipAddress);
BigInteger start = new BigInteger(1, this.startAddress.getAddress()); BigInteger start = new BigInteger(1, this.startAddress.getAddress());
@@ -139,4 +132,5 @@ public class CIDRUtils {
return (st == -1 || st == 0) && (te == -1 || te == 0); return (st == -1 || st == 0) && (te == -1 || te == 0);
} }
} }

View File

@@ -0,0 +1,33 @@
/*
* matrix-java-sdk - Matrix Client SDK for Java
* Copyright (C) 2018 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;
public class MalformedEventException extends MatrixException {
public MalformedEventException(String s) {
super("M_NOT_JSON", s);
}
public static MalformedEventException forId(String id) {
return new MalformedEventException("Event " + id + " is malformed");
}
}

View File

@@ -0,0 +1,44 @@
/*
* 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;
public class MatrixErrorInfo {
private String errcode;
private String error;
public MatrixErrorInfo(String errcode) {
this.errcode = errcode;
}
public MatrixErrorInfo(Throwable t) {
this.errcode = "AS_INTERNAL_SERVER_ERROR";
}
public String getErrcode() {
return errcode;
}
public String getError() {
return error;
}
}

View File

@@ -0,0 +1,48 @@
/*
* matrix-java-sdk - Matrix Client SDK for Java
* Copyright (C) 2018 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;
public class MatrixException extends RuntimeException {
private String errorCode;
private String error;
public MatrixException(String errorCode, String error) {
super(errorCode + ": " + error);
this.errorCode = errorCode;
this.error = error;
}
public MatrixException(String errorCode, String error, Throwable t) {
super(errorCode + ": " + error, t);
this.errorCode = errorCode;
this.error = error;
}
public String getErrorCode() {
return errorCode;
}
public String getError() {
return error;
}
}

View File

@@ -0,0 +1,170 @@
/*
* 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;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class MatrixID implements _MatrixID {
public static class Builder {
private MatrixID mxId;
public Builder(String id) {
mxId = MatrixID.parse(id);
}
public MatrixID valid() {
if (!mxId.isValid()) {
throw new IllegalArgumentException(mxId + " is not a valid Matrix ID");
}
return mxId;
}
public MatrixID acceptable() {
if (!mxId.isAcceptable()) {
throw new IllegalArgumentException(mxId + " is not an acceptable Matrix ID");
}
return mxId;
}
}
public static final String ALLOWED_CHARS = "0-9a-z-.=_/";
public static final Pattern LAX_PATTERN = Pattern.compile("@(.*?):(.+)");
public static final Pattern STRICT_PATTERN = Pattern.compile("@([" + ALLOWED_CHARS + "]+):(.+)");
private String id;
private String localpart;
private String domain;
private static String buildRaw(String localpart, String domain) {
return "@" + localpart + ":" + domain;
}
private static MatrixID parse(String id) {
Matcher m = LAX_PATTERN.matcher(id);
if (!m.matches()) {
throw new IllegalArgumentException(id + " is not a Matrix ID");
}
MatrixID mxId = new MatrixID();
mxId.id = id;
mxId.localpart = m.group(1);
mxId.domain = m.group(2);
return mxId;
}
public static Builder from(String id) {
return new Builder(id);
}
public static Builder from(String local, String domain) {
return from(buildRaw(local, domain));
}
public static MatrixID asValid(String id) {
return new Builder(id).valid();
}
public static MatrixID asAcceptable(String local, String domain) {
return from(local, domain).acceptable();
}
public static MatrixID asAcceptable(String id) {
return from(id).acceptable();
}
private MatrixID() {
// not for public consumption
}
private MatrixID(MatrixID mxId) {
this.id = mxId.id;
this.localpart = mxId.localpart;
this.domain = mxId.domain;
}
@Deprecated
public MatrixID(String mxId) {
this(parse(mxId));
}
@Deprecated
public MatrixID(String localpart, String domain) {
this(parse(buildRaw(localpart, domain)));
}
@Override
public String getId() {
return id;
}
@Override
public String getLocalPart() {
return localpart;
}
@Override
public String getDomain() {
return domain;
}
@Override
public MatrixID canonicalize() {
return parse(getId().toLowerCase());
}
@Override
public boolean isValid() {
return isAcceptable() && STRICT_PATTERN.matcher(id).matches();
}
@Override
public boolean isAcceptable() {
// TODO properly implement
return id.length() <= 255;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof MatrixID)) return false;
MatrixID matrixID = (MatrixID) o;
return id.equals(matrixID.id);
}
@Override
public int hashCode() {
return id.hashCode();
}
@Override
public String toString() {
return getId();
}
}

View File

@@ -0,0 +1,93 @@
/*
* matrix-java-sdk - Matrix Client SDK for Java
* Copyright (C) 2018 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;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import java.nio.charset.StandardCharsets;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static io.kamax.matrix.MatrixID.ALLOWED_CHARS;
public class MatrixIdCodec {
public static final String DELIMITER = "=";
public static final String ENCODE_REGEX = "[^" + ALLOWED_CHARS + "]+";
public static final Pattern ENCODE_PATTERN = Pattern.compile(ENCODE_REGEX);
public static final Pattern DECODE_PATTERN = Pattern.compile("(=[0-9a-f]{2})+");
private MatrixIdCodec() {
// not for public consumption
}
public static String encode(String decoded) {
decoded = decoded.toLowerCase();
StringBuilder builder = new StringBuilder();
for (Character c : decoded.toCharArray()) {
String s = c.toString();
Matcher lp = ENCODE_PATTERN.matcher(s);
if (!lp.find()) {
builder.append(s);
} else {
for (byte b : c.toString().getBytes(StandardCharsets.UTF_8)) {
builder.append(DELIMITER);
builder.append(Hex.encodeHexString(new byte[] { b }));
}
}
}
return builder.toString();
}
public static String decode(String encoded) {
StringBuilder builder = new StringBuilder();
Matcher m = DECODE_PATTERN.matcher(encoded);
int prevEnd = 0;
while (m.find()) {
try {
int start = m.start();
int end = m.end();
String sub = encoded.substring(start, end).replaceAll(DELIMITER, "");
String decoded = new String(Hex.decodeHex(sub.toCharArray()), StandardCharsets.UTF_8);
builder.append(encoded, prevEnd, start);
builder.append(decoded);
prevEnd = end - 1;
} catch (DecoderException e) {
e.printStackTrace();
}
}
prevEnd++;
if (prevEnd < encoded.length()) {
builder.append(encoded, prevEnd, encoded.length());
}
if (builder.length() == 0) {
return encoded;
} else {
return builder.toString();
}
}
}

View File

@@ -0,0 +1,104 @@
/*
* matrix-java-sdk - Matrix Client SDK for Java
* 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.matrix;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
public class MatrixPath {
public static String encode(String element) {
try {
return URLEncoder.encode(element, StandardCharsets.UTF_8.name());
} catch (UnsupportedEncodingException e) {
// This would be madness if it happened, but we need to handle it still
throw new RuntimeException(e);
}
}
private static MatrixPath with(String base) {
return new MatrixPath().add(base);
}
public static MatrixPath root() {
return with("");
}
public static MatrixPath base() {
return root().add("_matrix");
}
public static MatrixPath client() {
return base().add("client");
}
public static MatrixPath clientR0() {
return client().add("r0");
}
private StringBuilder path = new StringBuilder();
/**
* Add the raw element to this path
*
* @param element
* The raw element to be added as is to the path, without encoding or path separator
* @return The MatrixPath
*/
public MatrixPath put(String element) {
path.append(element);
return this;
}
/**
* URL encode and add a new path element
*
* This method handle path separators
*
* @param element
* The element to be encoded and added.
* @return The MatrixPath
*/
public MatrixPath add(String element) {
// We add a path separator if this is the first character or if the last character is not a path separator
// already
if (path.length() == 0 || path.lastIndexOf("/", 0) < path.length() - 1) {
put("/");
}
put(encode(element));
return this;
}
public String get() {
return path.toString();
}
public String toString() {
return get();
}
public URI toURI() {
return URI.create(toString());
}
}

View File

@@ -0,0 +1,61 @@
/*
* 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;
public class ThreePid implements _ThreePid {
private String medium;
private String address;
public ThreePid(String medium, String address) {
this.medium = medium;
this.address = address;
}
@Override
public String getMedium() {
return medium;
}
@Override
public String getAddress() {
return address;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ThreePid threePid = (ThreePid) 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

@@ -0,0 +1,61 @@
/*
* 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;
public class ThreePidMapping implements _ThreePidMapping {
private _ThreePid threePid;
private _MatrixID mxId;
public ThreePidMapping(_ThreePid threePid, _MatrixID mxId) {
this.threePid = threePid;
this.mxId = mxId;
}
@Override
public _ThreePid getThreePid() {
return threePid;
}
@Override
public _MatrixID getMatrixId() {
return mxId;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ThreePidMapping that = (ThreePidMapping) o;
if (!threePid.equals(that.threePid)) return false;
return mxId.equals(that.mxId);
}
@Override
public int hashCode() {
int result = threePid.hashCode();
result = 31 * result + mxId.hashCode();
return result;
}
}

View File

@@ -0,0 +1,46 @@
/*
* 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;
public enum ThreePidMedium {
Email("email"),
PhoneNumber("msisdn");
private String id;
ThreePidMedium(String id) {
this.id = id;
}
public String getId() {
return id;
}
public String toString() {
return getId();
}
public boolean is(String s) {
return getId().contentEquals(s);
}
}

View File

@@ -0,0 +1,41 @@
/*
* 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;
import java.net.URI;
import java.net.URL;
import java.util.Optional;
public interface _MatrixContent {
URI getAddress();
URL getPermaLink();
boolean isValid();
Optional<String> getType();
byte[] getData();
Optional<String> getFilename();
}

View File

@@ -0,0 +1,55 @@
/*
* 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;
public interface _MatrixID {
String getId();
String getLocalPart();
String getDomain();
/**
* Render this Matrix ID strictly valid. In technical term, transform this ID so
* <code>isValid()</code> returns true.
*
* @return A canonical Matrix ID
*/
_MatrixID canonicalize();
/**
* If the Matrix ID is strictly valid in the protocol as per
* http://matrix.org/docs/spec/intro.html#user-identifiers
*
* @return true if strictly valid, false if not
*/
boolean isValid();
/**
* If the Matrix ID is acceptable in the protocol as per
* http://matrix.org/docs/spec/intro.html#historical-user-ids
*
* @return
*/
boolean isAcceptable();
}

View File

@@ -0,0 +1,31 @@
/*
* 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;
import io.kamax.matrix.client._Presence;
import java.util.Optional;
public interface _MatrixUser extends _MatrixUserProfile {
Optional<_Presence> getPresence();
}

View File

@@ -0,0 +1,42 @@
/*
* matrix-java-sdk - Matrix SDK for Java
* Copyright (C) 2018 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;
import java.net.URI;
import java.util.Optional;
public interface _MatrixUserProfile {
_MatrixID getId();
Optional<String> getName();
void setName(String name);
Optional<String> getAvatarUrl();
void setAvatar(String avatarRef);
void setAvatar(URI avatarUri);
Optional<_MatrixContent> getAvatar();
}

View File

@@ -0,0 +1,29 @@
/*
* 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;
public interface _ThreePid {
String getMedium();
String getAddress();
}

View File

@@ -0,0 +1,29 @@
/*
* 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;
public interface _ThreePidMapping {
_ThreePid getThreePid();
_MatrixID getMatrixId();
}

View File

@@ -0,0 +1,409 @@
/*
* 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.client;
import com.google.gson.Gson;
import com.google.gson.JsonParser;
import com.google.gson.JsonSyntaxException;
import io.kamax.matrix.MatrixErrorInfo;
import io.kamax.matrix._MatrixID;
import io.kamax.matrix.hs._MatrixHomeserver;
import io.kamax.matrix.json.GsonUtil;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.net.URL;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.Objects;
import java.util.Optional;
import okhttp3.*;
public abstract class AMatrixHttpClient implements _MatrixClientRaw {
private Logger log = LoggerFactory.getLogger(AMatrixHttpClient.class);
protected MatrixClientContext context;
protected Gson gson = GsonUtil.get();
protected JsonParser jsonParser = new JsonParser();
private OkHttpClient client;
public AMatrixHttpClient(String domain) {
this(new MatrixClientContext().setDomain(domain));
}
public AMatrixHttpClient(URL hsBaseUrl) {
this(new MatrixClientContext().setHsBaseUrl(hsBaseUrl));
}
protected AMatrixHttpClient(MatrixClientContext context) {
this(context, new OkHttpClient.Builder(), new MatrixClientDefaults());
}
protected AMatrixHttpClient(MatrixClientContext context, OkHttpClient.Builder client) {
this(context, client, new MatrixClientDefaults());
}
protected AMatrixHttpClient(MatrixClientContext context, OkHttpClient.Builder client,
MatrixClientDefaults defaults) {
this(context, client.connectTimeout(defaults.getConnectTimeout(), TimeUnit.MILLISECONDS)
.readTimeout(5, TimeUnit.MINUTES).followRedirects(false).build());
}
protected AMatrixHttpClient(MatrixClientContext context, OkHttpClient client) {
this.context = context;
this.client = client;
}
@Override
public Optional<_AutoDiscoverySettings> discoverSettings() {
if (StringUtils.isBlank(context.getDomain())) {
throw new IllegalStateException("A non-empty Matrix domain must be set to discover the client settings");
}
String hostname = context.getDomain().split(":")[0];
log.info("Performing .well-known auto-discovery for {}", hostname);
URL url = new HttpUrl.Builder().scheme("https").host(hostname).addPathSegments(".well-known/matrix/client")
.build().url();
String body = execute(new MatrixHttpRequest(new Request.Builder().get().url(url)).addIgnoredErrorCode(404));
if (StringUtils.isBlank(body)) {
if (Objects.isNull(context.getHsBaseUrl())) {
throw new IllegalStateException("No valid Homeserver base URL was found");
}
// No .well-known data found
// FIXME improve SDK so we can differentiate between not found and empty.
// not found = skip
// empty = failure
return Optional.empty();
}
log.info("Found body: {}", body);
WellKnownAutoDiscoverySettings settings = new WellKnownAutoDiscoverySettings(body);
log.info("Found .well-known data");
// TODO reconsider if and where we should check for an already present HS url in the context
if (settings.getHsBaseUrls().isEmpty()) {
throw new IllegalStateException("No valid Homeserver base URL was found");
}
for (URL baseUrlCandidate : settings.getHsBaseUrls()) {
context.setHsBaseUrl(baseUrlCandidate);
try {
if (!getHomeApiVersions().isEmpty()) {
log.info("Found a valid HS at {}", getContext().getHsBaseUrl().toString());
break;
}
} catch (MatrixClientRequestException e) {
log.warn("Error when trying to fetch {}: {}", baseUrlCandidate, e.getMessage());
}
}
for (URL baseUrlCandidate : settings.getIsBaseUrls()) {
context.setIsBaseUrl(baseUrlCandidate);
try {
if (validateIsBaseUrl()) {
log.info("Found a valid IS at {}", getContext().getIsBaseUrl().toString());
break;
}
} catch (MatrixClientRequestException e) {
log.warn("Error when trying to fetch {}: {}", baseUrlCandidate, e.getMessage());
}
}
return Optional.of(settings);
}
@Override
public MatrixClientContext getContext() {
return context;
}
@Override
public _MatrixHomeserver getHomeserver() {
return context.getHomeserver();
}
@Override
public Optional<String> getAccessToken() {
return Optional.ofNullable(context.getToken());
}
public String getAccessTokenOrThrow() {
return getAccessToken()
.orElseThrow(() -> new IllegalStateException("This method can only be used with a valid token."));
}
@Override
public List<String> getHomeApiVersions() {
String body = execute(new Request.Builder().get().url(getPath("client", "", "versions")));
return GsonUtil.asList(GsonUtil.parseObj(body), "versions", String.class);
}
@Override
public boolean validateIsBaseUrl() {
String body = execute(new Request.Builder().get().url(getIdentityPath("identity", "api", "v1")));
return "{}".equals(body);
}
protected String getUserId() {
return getUser().orElseThrow(IllegalStateException::new).getId();
}
@Override
public Optional<_MatrixID> getUser() {
return context.getUser();
}
protected Request.Builder addAuthHeader(Request.Builder builder, String token) {
builder.addHeader("Authorization", "Bearer " + token);
return builder;
}
protected Request.Builder addAuthHeader(Request.Builder builder) {
return addAuthHeader(builder, getAccessTokenOrThrow());
}
protected String executeAuthenticated(Request.Builder builder, String token) {
return execute(addAuthHeader(builder, token));
}
protected String executeAuthenticated(Request.Builder builder) {
return execute(addAuthHeader(builder));
}
protected String executeAuthenticated(MatrixHttpRequest matrixRequest) {
addAuthHeader(matrixRequest.getHttpRequest());
return execute(matrixRequest);
}
protected String execute(Request.Builder builder) {
return execute(new MatrixHttpRequest(builder));
}
protected String execute(MatrixHttpRequest matrixRequest) {
log(matrixRequest.getHttpRequest());
try (Response response = client.newCall(matrixRequest.getHttpRequest().build()).execute()) {
String body = response.body().string();
int responseStatus = response.code();
if (responseStatus == 200) {
log.debug("Request successfully executed.");
} else if (matrixRequest.getIgnoredErrorCodes().contains(responseStatus)) {
log.debug("Error code ignored: " + responseStatus);
return "";
} else {
MatrixErrorInfo info = createErrorInfo(body, responseStatus);
body = handleError(matrixRequest, responseStatus, info);
}
return body;
} catch (IOException e) {
throw new MatrixClientRequestException(e);
}
}
protected MatrixHttpContentResult executeContentRequest(MatrixHttpRequest matrixRequest) {
log(matrixRequest.getHttpRequest());
try (Response response = client.newCall(matrixRequest.getHttpRequest().build()).execute()) {
int responseStatus = response.code();
MatrixHttpContentResult result;
if (responseStatus == 200 || matrixRequest.getIgnoredErrorCodes().contains(responseStatus)) {
log.debug("Request successfully executed.");
result = new MatrixHttpContentResult(response);
} else {
String body = response.body().string();
MatrixErrorInfo info = createErrorInfo(body, responseStatus);
result = handleErrorContentRequest(matrixRequest, responseStatus, info);
}
return result;
} catch (IOException e) {
throw new MatrixClientRequestException(e);
}
}
/**
* Default handling of errors. Can be overwritten by a custom implementation in inherited classes.
*
* @param matrixRequest
* @param responseStatus
* @param info
* @return body of the response of a repeated call of the request, else this methods throws a
* MatrixClientRequestException
*/
protected String handleError(MatrixHttpRequest matrixRequest, int responseStatus, MatrixErrorInfo info) {
String message = String.format("Request failed: %s", responseStatus);
if (Objects.nonNull(info)) {
message = String.format("%s - %s - %s", message, info.getErrcode(), info.getError());
}
if (responseStatus == 429) {
return handleRateLimited(matrixRequest, info);
}
throw new MatrixClientRequestException(info, message);
}
/**
* Default handling of rate limited calls. Can be overwritten by a custom implementation in inherited classes.
*
* @param matrixRequest
* @param info
* @return body of the response of a repeated call of the request, else this methods throws a
* MatrixClientRequestException
*/
protected String handleRateLimited(MatrixHttpRequest matrixRequest, MatrixErrorInfo info) {
throw new MatrixClientRequestException(info, "Request was rate limited.");
// TODO Add default handling of rate limited call, i.e. repeated call after given time interval.
// 1. Wait for timeout
// 2. return execute(request)
}
protected MatrixHttpContentResult handleErrorContentRequest(MatrixHttpRequest matrixRequest, int responseStatus,
MatrixErrorInfo info) {
String message = String.format("Request failed with status code: %s", responseStatus);
if (responseStatus == 429) {
return handleRateLimitedContentRequest(matrixRequest, info);
}
throw new MatrixClientRequestException(info, message);
}
protected MatrixHttpContentResult handleRateLimitedContentRequest(MatrixHttpRequest matrixRequest,
MatrixErrorInfo info) {
throw new MatrixClientRequestRateLimitedException(info, "Request was rate limited.");
// TODO Add default handling of rate limited call, i.e. repeated call after given time interval.
// 1. Wait for timeout
// 2. return execute(request)
}
protected Optional<String> extractAsStringFromBody(String body, String jsonObjectName) {
if (StringUtils.isEmpty(body)) {
return Optional.empty();
}
return GsonUtil.findString(jsonParser.parse(body).getAsJsonObject(), jsonObjectName);
}
private MatrixErrorInfo createErrorInfo(String body, int responseStatus) {
MatrixErrorInfo info = null;
try {
info = gson.fromJson(body, MatrixErrorInfo.class);
if (Objects.nonNull(info)) {
log.debug("Request returned with an error. Status code: {}, errcode: {}, error: {}", responseStatus,
info.getErrcode(), info.getError());
}
} catch (JsonSyntaxException e) {
log.debug("Unable to parse Matrix error info. Content was:\n{}", body);
}
return info;
}
private void log(Request.Builder req) {
log.debug("Doing {} {}", req, req.toString());
}
protected HttpUrl.Builder getHsBaseUrl() {
return HttpUrl.get(context.getHsBaseUrl()).newBuilder();
}
protected HttpUrl.Builder getIsBaseUrl() {
return HttpUrl.get(context.getIsBaseUrl()).newBuilder();
}
protected HttpUrl.Builder getPathBuilder(HttpUrl.Builder base, String... segments) {
base.addPathSegment("_matrix");
for (String segment : segments) {
base.addPathSegment(segment);
}
if (context.isVirtual()) {
context.getUser().ifPresent(user -> base.addQueryParameter("user_id", user.getId()));
}
return base;
}
protected HttpUrl.Builder getPathBuilder(String... segments) {
return getPathBuilder(getHsBaseUrl(), segments);
}
protected HttpUrl.Builder getIdentityPathBuilder(String... segments) {
return getPathBuilder(getIsBaseUrl(), segments);
}
protected URL getPath(String... segments) {
return getPathBuilder(segments).build().url();
}
protected URL getIdentityPath(String... segments) {
return getIdentityPathBuilder(segments).build().url();
}
protected HttpUrl.Builder getClientPathBuilder(String... segments) {
String[] base = { "client", "r0" };
segments = ArrayUtils.addAll(base, segments);
return getPathBuilder(segments);
}
protected HttpUrl.Builder getMediaPathBuilder(String... segments) {
String[] base = { "media", "r0" };
segments = ArrayUtils.addAll(base, segments);
return getPathBuilder(segments);
}
protected URL getClientPath(String... segments) {
return getClientPathBuilder(segments).build().url();
}
protected URL getMediaPath(String... segments) {
return getMediaPathBuilder(segments).build().url();
}
protected RequestBody getJsonBody(Object o) {
return RequestBody.create(MediaType.parse("application/json"), GsonUtil.get().toJson(o));
}
protected Request.Builder request(URL url) {
return new Request.Builder().url(url);
}
protected Request.Builder getRequest(URL url) {
return request(url).get();
}
}

View File

@@ -0,0 +1,154 @@
/*
* 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.client;
import io.kamax.matrix.MatrixID;
import io.kamax.matrix._MatrixID;
import io.kamax.matrix.hs.MatrixHomeserver;
import io.kamax.matrix.hs._MatrixHomeserver;
import java.net.URL;
import java.util.Objects;
import java.util.Optional;
public class MatrixClientContext {
private String domain;
private URL hsBaseUrl;
private URL isBaseUrl;
private _MatrixID user;
private String token;
private boolean isVirtual;
private String deviceId;
private String initialDeviceName;
public MatrixClientContext() {
// stub
}
public MatrixClientContext(MatrixClientContext other) {
this.domain = other.domain;
this.hsBaseUrl = other.hsBaseUrl;
this.isBaseUrl = other.isBaseUrl;
this.user = other.user;
this.token = other.token;
this.isVirtual = other.isVirtual;
this.deviceId = other.deviceId;
this.initialDeviceName = other.initialDeviceName;
}
public MatrixClientContext(_MatrixHomeserver hs) {
setDomain(hs.getDomain());
setHsBaseUrl(hs.getBaseEndpoint());
}
public MatrixClientContext(_MatrixHomeserver hs, _MatrixID user, String token) {
this(hs);
setUser(user);
setToken(token);
}
public _MatrixHomeserver getHomeserver() {
if (Objects.isNull(hsBaseUrl)) {
throw new IllegalStateException("Homeserver Base URL is not set");
}
return new MatrixHomeserver(domain, hsBaseUrl.toString());
}
public String getDomain() {
return domain;
}
public MatrixClientContext setDomain(String domain) {
this.domain = domain;
return this;
}
public URL getHsBaseUrl() {
return hsBaseUrl;
}
public MatrixClientContext setHsBaseUrl(URL hsBaseUrl) {
this.hsBaseUrl = hsBaseUrl;
return this;
}
public URL getIsBaseUrl() {
return isBaseUrl;
}
public MatrixClientContext setIsBaseUrl(URL isBaseUrl) {
this.isBaseUrl = isBaseUrl;
return this;
}
public Optional<_MatrixID> getUser() {
return Optional.ofNullable(user);
}
public MatrixClientContext setUser(_MatrixID user) {
this.user = user;
return this;
}
public MatrixClientContext setUserWithLocalpart(String localpart) {
setUser(MatrixID.asAcceptable(localpart, getDomain()));
return this;
}
public String getToken() {
return token;
}
public MatrixClientContext setToken(String token) {
this.token = token;
return this;
}
public boolean isVirtual() {
return isVirtual;
}
public MatrixClientContext setVirtual(boolean virtual) {
isVirtual = virtual;
return this;
}
public String getDeviceId() {
return deviceId;
}
public MatrixClientContext setDeviceId(String deviceId) {
this.deviceId = deviceId;
return this;
}
public String getInitialDeviceName() {
return initialDeviceName;
}
public MatrixClientContext setInitialDeviceName(String initialDeviceName) {
this.initialDeviceName = initialDeviceName;
return this;
}
}

View File

@@ -0,0 +1,59 @@
/*
* matrix-java-sdk - Matrix Client SDK for Java
* Copyright (C) 2018 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.client;
public class MatrixClientDefaults {
private int connectTimeout = 30 * 1000; // 30 sec
private int requestTimeout = 5 * 60 * 1000; // 5 min
private int socketTimeout = requestTimeout;
public int getConnectTimeout() {
return connectTimeout;
}
public MatrixClientDefaults setConnectTimeout(int connectTimeout) {
this.connectTimeout = connectTimeout;
return this;
}
public int getRequestTimeout() {
return requestTimeout;
}
public MatrixClientDefaults setRequestTimeout(int requestTimeout) {
this.requestTimeout = requestTimeout;
return this;
}
public int getSocketTimeout() {
return socketTimeout;
}
public MatrixClientDefaults setSocketTimeout(int socketTimeout) {
this.socketTimeout = socketTimeout;
return this;
}
}

View File

@@ -0,0 +1,46 @@
/*
* 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.client;
import io.kamax.matrix.MatrixErrorInfo;
import java.io.IOException;
import java.util.Optional;
public class MatrixClientRequestException extends RuntimeException {
private MatrixErrorInfo errorInfo;
public MatrixClientRequestException(IOException e) {
super(e);
}
public MatrixClientRequestException(MatrixErrorInfo errorInfo, String message) {
super(message);
this.errorInfo = errorInfo;
}
public Optional<MatrixErrorInfo> getError() {
return Optional.ofNullable(errorInfo);
}
}

View File

@@ -0,0 +1,31 @@
/*
* matrix-java-sdk - Matrix Client SDK for Java
* Copyright (C) 2018 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.client;
import io.kamax.matrix.MatrixErrorInfo;
public class MatrixClientRequestRateLimitedException extends MatrixClientRequestException {
public MatrixClientRequestRateLimitedException(MatrixErrorInfo errorInfo, String message) {
super(errorInfo, message);
}
}

View File

@@ -0,0 +1,133 @@
/*
* 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.client;
import io.kamax.matrix._MatrixContent;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.URI;
import java.net.URL;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.Optional;
import okhttp3.Request;
public class MatrixHttpContent extends AMatrixHttpClient implements _MatrixContent {
private Logger log = LoggerFactory.getLogger(MatrixHttpContent.class);
private final Pattern filenamePattern = Pattern.compile("filename=\"?([^\";]+)");
private URI address;
private MatrixHttpContentResult result;
private boolean loaded = false;
private boolean valid = false;
public MatrixHttpContent(MatrixClientContext context, URI address) {
super(context);
this.address = address;
}
// TODO switch a HTTP HEAD to fetch initial data, instead of loading in memory directly
private synchronized void load() {
if (loaded) {
return;
}
try {
if (!StringUtils.equalsIgnoreCase("mxc", address.getScheme())) {
log.debug("{} is not a supported protocol for avatars, ignoring", address.getScheme());
} else {
MatrixHttpRequest request = new MatrixHttpRequest(new Request.Builder().url(getPermaLink()));
result = executeContentRequest(request);
valid = result.isValid();
}
} catch (MatrixClientRequestException e) {
valid = false;
}
loaded = true;
}
@Override
public URI getAddress() {
return address;
}
@Override
public URL getPermaLink() {
return getMediaPathBuilder("download", address.getHost()).addEncodedPathSegments(address.getPath().substring(1))
.build().url();
}
@Override
public boolean isValid() {
load();
return valid;
}
@Override
public Optional<String> getType() {
load();
if (!isValid()) {
throw new IllegalStateException("This method should only be called, if valid is true.");
}
return result.getContentType();
}
@Override
public byte[] getData() {
load();
if (!isValid()) {
throw new IllegalStateException("This method should only be called, if valid is true.");
}
return result.getData();
}
@Override
public Optional<String> getFilename() {
load();
if (!isValid()) {
throw new IllegalStateException("This method should only be called, if valid is true.");
}
return result.getHeader("Content-Disposition").filter(l -> !l.isEmpty()).flatMap(l -> {
for (String v : l) {
Matcher m = filenamePattern.matcher(v);
if (m.find()) {
return Optional.of(m.group(1));
}
}
return Optional.empty();
});
}
}

View File

@@ -0,0 +1,71 @@
/*
* 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.client;
import java.io.IOException;
import java.util.*;
import java.util.Optional;
import okhttp3.MediaType;
import okhttp3.Response;
import okhttp3.ResponseBody;
public class MatrixHttpContentResult {
private final boolean valid;
private final Map<String, List<String>> headers;
private final Optional<String> contentType;
private final byte[] data;
public MatrixHttpContentResult(Response response) throws IOException {
try (ResponseBody body = response.body()) {
boolean hasBody = Objects.nonNull(body);
valid = hasBody && response.code() == 200;
headers = response.headers().toMultimap();
if (hasBody) {
contentType = Optional.ofNullable(body.contentType()).map(MediaType::toString);
data = body.bytes();
} else {
contentType = Optional.empty();
data = new byte[0];
}
}
}
public boolean isValid() {
return valid;
}
public Optional<List<String>> getHeader(String name) {
return Optional.ofNullable(headers.get(name));
}
public Optional<String> getContentType() {
return contentType;
}
public byte[] getData() {
return data;
}
}

View File

@@ -0,0 +1,93 @@
/*
* matrix-java-sdk - Matrix Client SDK for Java
* Copyright (C) 2018 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.client;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import io.kamax.matrix.json.GsonUtil;
import org.apache.commons.lang3.ArrayUtils;
import java.net.URL;
import java.util.List;
import javax.swing.*;
public class MatrixHttpPushRule extends AMatrixHttpClient implements _PushRule {
private static final String ActionKey = "actions";
private static final String EnabledKey = "enabled";
private final String[] baseSegments;
public MatrixHttpPushRule(MatrixClientContext context, String scope, String kind, String id) {
super(context);
baseSegments = new String[] { "pushrules", scope, kind, id };
}
private URL makeUrl() {
return getClientPath(baseSegments);
}
private URL makeUrl(String... segments) {
return getClientPath(ArrayUtils.addAll(baseSegments, segments));
}
@Override
public JsonObject getJson() {
return GsonUtil.parseObj(executeAuthenticated(getRequest(makeUrl())));
}
@Override
public void set(JsonObject data) {
executeAuthenticated(request(makeUrl()).put(getJsonBody(data)));
}
@Override
public void delete() {
executeAuthenticated(request(makeUrl()).delete());
}
@Override
public boolean isEnabled() {
JsonObject response = GsonUtil.parseObj(executeAuthenticated(getRequest(makeUrl(EnabledKey))));
return GsonUtil.getPrimitive(response, EnabledKey).getAsBoolean();
}
@Override
public void setEnabled(boolean enabled) {
executeAuthenticated(request(makeUrl(EnabledKey)).put(getJsonBody(GsonUtil.makeObj(EnabledKey, enabled))));
}
@Override
public List<String> getActions() {
JsonObject response = GsonUtil.parseObj(executeAuthenticated(getRequest(makeUrl(ActionKey))));
return GsonUtil.asList(GsonUtil.findArray(response, ActionKey).orElseGet(JsonArray::new), String.class);
}
@Override
public void setActions(List<String> data) {
executeAuthenticated(
request(makeUrl(ActionKey)).put(getJsonBody(GsonUtil.makeObj(ActionKey, GsonUtil.asArray(data)))));
}
}

View File

@@ -0,0 +1,50 @@
/*
* 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.client;
import java.util.ArrayList;
import java.util.List;
import okhttp3.Request;
public class MatrixHttpRequest {
private final Request.Builder httpRequest;
private List<Integer> ignoredErrorCodes = new ArrayList<>();
public MatrixHttpRequest(Request.Builder request) {
this.httpRequest = request;
}
public MatrixHttpRequest addIgnoredErrorCode(int errcode) {
ignoredErrorCodes.add(errcode);
return this;
}
public Request.Builder getHttpRequest() {
return httpRequest;
}
public List<Integer> getIgnoredErrorCodes() {
return ignoredErrorCodes;
}
}

View File

@@ -0,0 +1,440 @@
/*
* 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.client;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import io.kamax.matrix.MatrixErrorInfo;
import io.kamax.matrix.MatrixID;
import io.kamax.matrix._MatrixContent;
import io.kamax.matrix._MatrixID;
import io.kamax.matrix._MatrixUserProfile;
import io.kamax.matrix.hs._MatrixRoom;
import io.kamax.matrix.json.GsonUtil;
import io.kamax.matrix.json.RoomMessageChunkResponseJson;
import io.kamax.matrix.json.RoomMessageFormattedTextPutBody;
import io.kamax.matrix.json.RoomMessageTextPutBody;
import io.kamax.matrix.json.RoomTagSetBody;
import io.kamax.matrix.json.event.MatrixJsonPersistentEvent;
import io.kamax.matrix.room.*;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import okhttp3.HttpUrl;
import okhttp3.Request;
public class MatrixHttpRoom extends AMatrixHttpClient implements _MatrixRoom {
private Logger log = LoggerFactory.getLogger(MatrixHttpRoom.class);
private String roomId;
public MatrixHttpRoom(MatrixClientContext context, String roomId) {
super(context);
this.roomId = roomId;
}
@Override
public String getAddress() {
return roomId;
}
@Override
public Optional<String> getName() {
return getState("m.room.name").flatMap(obj -> GsonUtil.findString(obj, "name"));
}
@Override
public Optional<String> getTopic() {
return getState("m.room.topic").flatMap(obj -> GsonUtil.findString(obj, "topic"));
}
@Override
public Optional<String> getAvatarUrl() {
return getState("m.room.avatar").flatMap(obj -> GsonUtil.findString(obj, "url"));
}
@Override
public Optional<_MatrixContent> getAvatar() {
return getAvatarUrl().flatMap(url -> {
try {
return Optional.of(new MatrixHttpContent(context, new URI(url)));
} catch (URISyntaxException e) {
log.debug("{} is not a valid URI for avatar, returning empty", url);
return Optional.empty();
}
});
}
@Override
public String getId() {
return roomId;
}
@Override
public Optional<JsonObject> getState(String type) {
URL path = getClientPath("rooms", getAddress(), "state", type);
MatrixHttpRequest request = new MatrixHttpRequest(new Request.Builder().get().url(path));
request.addIgnoredErrorCode(404);
String body = executeAuthenticated(request);
if (StringUtils.isBlank(body)) {
return Optional.empty();
}
return Optional.of(GsonUtil.parseObj(body));
}
@Override
public Optional<JsonObject> getState(String type, String key) {
URL path = getClientPath("rooms", roomId, "state", type, key);
MatrixHttpRequest request = new MatrixHttpRequest(new Request.Builder().get().url(path));
request.addIgnoredErrorCode(404);
String body = executeAuthenticated(request);
if (StringUtils.isBlank(body)) {
return Optional.empty();
}
return Optional.of(GsonUtil.parseObj(body));
}
@Override
public void join() {
join(Collections.emptyList());
}
@Override
public void join(List<String> servers) {
HttpUrl.Builder b = getClientPathBuilder("rooms", roomId, "join");
servers.forEach(server -> b.addQueryParameter("server_name", server));
executeAuthenticated(new Request.Builder().post(getJsonBody(new JsonObject())).url(b.build()));
}
@Override
public Optional<MatrixErrorInfo> tryJoin() {
return tryJoin(Collections.emptyList());
}
@Override
public Optional<MatrixErrorInfo> tryJoin(List<String> servers) {
try {
join(servers);
return Optional.empty();
} catch (MatrixClientRequestException e) {
return e.getError();
}
}
@Override
public void leave() {
URL path = getClientPath("rooms", roomId, "leave");
MatrixHttpRequest request = new MatrixHttpRequest(
new Request.Builder().post(getJsonBody(new JsonObject())).url(path));
// TODO Find a better way to handle room objects for unknown rooms
// Maybe throw exception?
// TODO implement method to check room existence - isValid() ?
// if (res.getStatusLine().getStatusCode() == 404) {
// log.warn("Room {} is not joined, ignoring call", roomId);
// return;
// }
request.addIgnoredErrorCode(404);
executeAuthenticated(request);
}
@Override
public Optional<MatrixErrorInfo> tryLeave() {
try {
leave();
return Optional.empty();
} catch (MatrixClientRequestException e) {
return e.getError();
}
}
@Override
public void kick(_MatrixID user) {
kick(user, null);
}
@Override
public void kick(_MatrixID user, String reason) {
JsonObject body = new JsonObject();
body.addProperty("user_id", user.getId());
body.addProperty("reason", reason);
URL path = getClientPath("rooms", roomId, "kick");
MatrixHttpRequest request = new MatrixHttpRequest(new Request.Builder().post(getJsonBody(body)).url(path));
executeAuthenticated(request);
}
@Override
public Optional<MatrixErrorInfo> tryKick(_MatrixID user) {
return tryKick(user, null);
}
@Override
public Optional<MatrixErrorInfo> tryKick(_MatrixID user, String reason) {
try {
kick(user, reason);
return Optional.empty();
} catch (MatrixClientRequestException e) {
return e.getError();
}
}
@Override
public String sendEvent(String type, JsonObject content) {
// FIXME URL encoding
URL path = getClientPath("rooms", roomId, "send", type, Long.toString(System.currentTimeMillis()));
String body = executeAuthenticated(new Request.Builder().put(getJsonBody(content)).url(path));
return GsonUtil.getStringOrThrow(GsonUtil.parseObj(body), "event_id");
}
private String sendMessage(RoomMessageTextPutBody content) {
return sendEvent("m.room.message", GsonUtil.makeObj(content));
}
@Override
public String sendText(String message) {
return sendMessage(new RoomMessageTextPutBody(message));
}
@Override
public String sendFormattedText(String formatted, String rawFallback) {
// TODO sanitize input
return sendMessage(new RoomMessageFormattedTextPutBody(rawFallback, formatted));
}
@Override
public String sendNotice(String message) {
return sendMessage(new RoomMessageTextPutBody("m.notice", message));
}
@Override
public String sendNotice(String formatted, String plain) {
// TODO sanitize input
return sendMessage(new RoomMessageFormattedTextPutBody("m.notice", plain, formatted));
}
@Override
public void sendReceipt(String type, String eventId) {
URL path = getClientPath("rooms", roomId, "receipt", type, eventId);
executeAuthenticated(new Request.Builder().post(getJsonBody(new JsonObject())).url(path));
}
@Override
public void sendReceipt(ReceiptType type, String eventId) {
sendReceipt(type.getId(), eventId);
}
@Override
public void sendReadReceipt(String eventId) {
sendReceipt(ReceiptType.Read, eventId);
}
@Override
public void invite(_MatrixID mxId) {
URL path = getClientPath("rooms", roomId, "invite");
executeAuthenticated(
new Request.Builder().post(getJsonBody(GsonUtil.makeObj("user_id", mxId.getId()))).url(path));
}
@Override
public List<_MatrixUserProfile> getJoinedUsers() {
URL path = getClientPath("rooms", roomId, "joined_members");
String body = executeAuthenticated(new Request.Builder().get().url(path));
List<_MatrixUserProfile> ids = new ArrayList<>();
if (StringUtils.isNotEmpty(body)) {
JsonObject joinedUsers = jsonParser.parse(body).getAsJsonObject().get("joined").getAsJsonObject();
ids = joinedUsers.entrySet().stream().filter(e -> e.getValue().isJsonObject()).map(entry -> {
JsonObject obj = entry.getValue().getAsJsonObject();
return new MatrixHttpUser(getContext(), MatrixID.asAcceptable(entry.getKey())) {
@Override
public Optional<String> getName() {
return GsonUtil.findString(obj, "display_name");
}
@Override
public Optional<_MatrixContent> getAvatar() {
return GsonUtil.findString(obj, "avatar_url").flatMap(s -> {
try {
return Optional.of(new URI(s));
} catch (URISyntaxException e) {
return Optional.empty();
}
}).map(uri -> new MatrixHttpContent(getContext(), uri));
}
};
}).collect(Collectors.toList());
}
return ids;
}
@Override
public _MatrixRoomMessageChunk getMessages(_MatrixRoomMessageChunkOptions options) {
HttpUrl.Builder builder = getClientPathBuilder("rooms", roomId, "messages");
builder.addQueryParameter("from", options.getFromToken());
builder.addQueryParameter("dir", options.getDirection());
options.getToToken().ifPresent(token -> builder.addQueryParameter("to", token));
options.getLimit().ifPresent(limit -> builder.addQueryParameter("limit", limit.toString()));
String bodyRaw = executeAuthenticated(new Request.Builder().get().url(builder.build().url()));
RoomMessageChunkResponseJson body = GsonUtil.get().fromJson(bodyRaw, RoomMessageChunkResponseJson.class);
return new MatrixRoomMessageChunk(body.getStart(), body.getEnd(),
body.getChunk().stream().map(MatrixJsonPersistentEvent::new).collect(Collectors.toList()));
}
@Override
public List<Tag> getUserTags() {
return getAllTags().stream().filter(tag -> "u".equals(tag.getNamespace()))
.collect(Collectors.toList());
}
@Override
public List<Tag> getAllTags() {
URL path = getClientPath("user", getUserId(), "rooms", getAddress(), "tags");
String body = executeAuthenticated(new Request.Builder().get().url(path));
JsonObject jsonTags = GsonUtil.parseObj(body).getAsJsonObject("tags").getAsJsonObject();
List<Tag> tags = jsonTags.entrySet().stream().map(entry -> {
String completeName = entry.getKey();
String name = "";
String namespace = "";
if (completeName.startsWith("m.")) {
name = completeName.substring(2);
namespace = "m";
} else if (completeName.startsWith("u.")) {
name = completeName.substring(2);
namespace = "u";
} else {
name = completeName;
}
JsonElement jsonOrder = entry.getValue().getAsJsonObject().get("order");
Double order = null;
if (jsonOrder != null) {
order = jsonOrder.getAsDouble();
}
return new Tag(namespace, name, order);
}).collect(Collectors.toList());
return tags;
}
@Override
public void addUserTag(String tag) {
addTag("u." + tag, null);
}
@Override
public void addUserTag(String tag, double order) {
addTag("u." + tag, order);
}
@Override
public void deleteUserTag(String tag) {
deleteTag("u." + tag);
}
@Override
public void addFavouriteTag() {
addTag("m.favourite", null);
}
@Override
public void addFavouriteTag(double order) {
addTag("m.favourite", order);
}
@Override
public Optional<Tag> getFavouriteTag() {
return getAllTags().stream()
.filter(tag -> "m".equals(tag.getNamespace()) && "favourite".equals(tag.getName())).findFirst();
}
@Override
public void deleteFavouriteTag() {
deleteTag("m.favourite");
}
@Override
public void addLowpriorityTag() {
addTag("m.lowpriority", null);
}
@Override
public void addLowpriorityTag(double order) {
addTag("m.lowpriority", order);
}
@Override
public Optional<Tag> getLowpriorityTag() {
return getAllTags().stream()
.filter(tag -> "m".equals(tag.getNamespace()) && "lowpriority".equals(tag.getName())).findFirst();
}
@Override
public void deleteLowpriorityTag() {
deleteTag("m.lowpriority");
}
private void addTag(String tag, Double order) {
// TODO check name size
if (order != null && (order < 0 || order > 1)) {
throw new IllegalArgumentException("Order out of range!");
}
URL path = getClientPath("user", getUserId(), "rooms", getAddress(), "tags", tag);
Request.Builder request = new Request.Builder().url(path);
if (order != null) {
request.put(getJsonBody(new RoomTagSetBody(order)));
} else {
request.put(getJsonBody(new JsonObject()));
}
executeAuthenticated(request);
}
private void deleteTag(String tag) {
URL path = getClientPath("user", getUserId(), "rooms", getAddress(), "tags", tag);
executeAuthenticated(new Request.Builder().url(path).delete());
}
}

View File

@@ -0,0 +1,125 @@
/*
* 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.client;
import com.google.gson.JsonObject;
import io.kamax.matrix._MatrixContent;
import io.kamax.matrix._MatrixID;
import io.kamax.matrix._MatrixUser;
import io.kamax.matrix.client.regular.Presence;
import io.kamax.matrix.json.GsonUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Optional;
import okhttp3.Request;
public class MatrixHttpUser extends AMatrixHttpClient implements _MatrixUser {
private Logger log = LoggerFactory.getLogger(MatrixHttpUser.class);
private _MatrixID mxId;
public MatrixHttpUser(MatrixClientContext context, _MatrixID mxId) {
super(context);
this.mxId = mxId;
}
@Override
public _MatrixID getId() {
return mxId;
}
@Override
public Optional<String> getName() {
URL path = getClientPath("profile", mxId.getId(), "displayname");
MatrixHttpRequest request = new MatrixHttpRequest(new Request.Builder().get().url(path));
request.addIgnoredErrorCode(404);
String body = executeAuthenticated(request);
return extractAsStringFromBody(body, "displayname");
}
@Override
public void setName(String name) {
URL path = getClientPath("profile", mxId.getId(), "displayname");
JsonObject body = GsonUtil.makeObj("displayname", name);
executeAuthenticated(new Request.Builder().put(getJsonBody(body)).url(path));
}
@Override
public Optional<String> getAvatarUrl() {
URL path = getClientPath("profile", mxId.getId(), "avatar_url");
MatrixHttpRequest request = new MatrixHttpRequest(new Request.Builder().get().url(path));
request.addIgnoredErrorCode(404);
String body = executeAuthenticated(request);
return extractAsStringFromBody(body, "avatar_url");
}
@Override
public void setAvatar(String avatarRef) {
URL path = getClientPath("profile", mxId.getId(), "avatar_url");
JsonObject body = GsonUtil.makeObj("avatar_url", avatarRef);
executeAuthenticated(new Request.Builder().put(getJsonBody(body)).url(path));
}
@Override
public void setAvatar(URI avatarUri) {
setAvatar(avatarUri.toString());
}
@Override
public Optional<_MatrixContent> getAvatar() {
return getAvatarUrl().flatMap(uri -> {
try {
return Optional.of(new MatrixHttpContent(getContext(), new URI(uri)));
} catch (URISyntaxException e) {
log.debug("{} is not a valid URI for avatar, returning empty", uri);
return Optional.empty();
}
});
}
@Override
public Optional<_Presence> getPresence() {
URL path = getClientPath("presence", mxId.getId(), "status");
MatrixHttpRequest request = new MatrixHttpRequest(new Request.Builder().get().url(path));
request.addIgnoredErrorCode(404);
String body = execute(request);
if (StringUtils.isBlank(body)) {
return Optional.empty();
}
return Optional.of(new Presence(GsonUtil.parseObj(body)));
}
}

View File

@@ -0,0 +1,39 @@
/*
* 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.client;
public class MatrixPasswordCredentials {
private final String localPart;
private final String password;
public MatrixPasswordCredentials(String localPart, String password) {
this.localPart = localPart;
this.password = password;
}
public String getLocalPart() {
return localPart;
}
public String getPassword() {
return password;
}
}

View File

@@ -0,0 +1,45 @@
/*
* matrix-java-sdk - Matrix Client SDK for Java
* 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.matrix.client;
import org.apache.commons.lang3.StringUtils;
public enum PresenceStatus {
Online("online"),
Offline("offline"),
Unavailable("unavailable");
private String id;
PresenceStatus(String id) {
this.id = id;
}
public String getId() {
return id;
}
public boolean is(String status) {
return StringUtils.equals(id, status);
}
}

View File

@@ -0,0 +1,138 @@
/*
* matrix-java-sdk - Matrix Client SDK for Java
* Copyright (C) 2018 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.client;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import io.kamax.matrix.json.GsonUtil;
import io.kamax.matrix.json.InvalidJsonException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
public class WellKnownAutoDiscoverySettings implements _AutoDiscoverySettings {
private final Logger log = LoggerFactory.getLogger(WellKnownAutoDiscoverySettings.class);
private JsonObject raw;
private List<URL> hsBaseUrls = new ArrayList<>();
private List<URL> isBaseUrls = new ArrayList<>();
/**
* Build .well-known auto-discovery settings from a .well-known source.
*
* @param raw
* The raw JSON data
* @throws IllegalArgumentException
* if the data is invalid and couldn't be parsed.
*/
public WellKnownAutoDiscoverySettings(String raw) {
try {
setRaw(GsonUtil.parseObj(raw));
} catch (JsonParseException | InvalidJsonException e) {
throw new IllegalArgumentException("Invalid JSON data for .well-known string");
}
}
private void setRaw(JsonObject raw) {
this.raw = raw;
process();
}
private Optional<URL> getUrl(String url) {
try {
return Optional.of(new URL(url));
} catch (MalformedURLException e) {
log.warn("Ignoring invalid Base URL entry in well-known: {} - {}", url, e.getMessage());
return Optional.empty();
}
}
private List<URL> getUrls(JsonArray array) {
List<URL> urls = new ArrayList<>();
array.forEach(el -> {
if (!el.isJsonPrimitive()) {
log.warn("Ignoring invalid Base URL entry in well-known: {} - Not a string", GsonUtil.get().toJson(el));
return;
}
getUrl(el.getAsString()).ifPresent(urls::add);
});
return urls;
}
private List<URL> processUrls(JsonObject base, String key) {
List<URL> urls = new ArrayList<>();
GsonUtil.findObj(base, key).ifPresent(cfg -> {
log.info("Found data");
GsonUtil.findArray(cfg, "base_urls").ifPresent(arr -> {
log.info("Found base URL(s)");
urls.addAll(getUrls(arr));
});
if (urls.isEmpty()) {
GsonUtil.findString(cfg, "base_url").flatMap(this::getUrl).ifPresent(urls::add);
}
});
return urls;
}
private void process() {
log.info("Processing Homeserver Base URLs");
hsBaseUrls = processUrls(raw, "m.homeserver");
log.info("Found {} valid URL(s)", hsBaseUrls.size());
log.info("Processing Identity server Base URLs");
isBaseUrls = processUrls(raw, "m.identity_server");
log.info("Found {} valid URL(s)", isBaseUrls.size());
}
@Override
public JsonObject getRaw() {
return raw;
}
@Override
public List<URL> getHsBaseUrls() {
return Collections.unmodifiableList(hsBaseUrls);
}
@Override
public List<URL> getIsBaseUrls() {
return Collections.unmodifiableList(isBaseUrls);
}
}

View File

@@ -0,0 +1,36 @@
/*
* matrix-java-sdk - Matrix Client SDK for Java
* Copyright (C) 2018 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.client;
import com.google.gson.JsonObject;
import java.net.URL;
import java.util.List;
public interface _AutoDiscoverySettings {
JsonObject getRaw();
List<URL> getHsBaseUrls();
List<URL> getIsBaseUrls();
}

View File

@@ -0,0 +1,39 @@
/*
* matrix-java-sdk - Matrix Client SDK for Java
* Copyright (C) 2018 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.client;
import com.google.gson.JsonObject;
import java.util.List;
public interface _GlobalPushRulesSet {
List<JsonObject> getContent();
List<JsonObject> getOverride();
List<JsonObject> getRoom();
List<JsonObject> getSender();
List<JsonObject> getUnderride();
}

View File

@@ -0,0 +1,158 @@
/*
* 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.client;
import com.google.gson.JsonObject;
import io.kamax.matrix._MatrixContent;
import io.kamax.matrix._MatrixID;
import io.kamax.matrix._MatrixUser;
import io.kamax.matrix.hs._MatrixRoom;
import io.kamax.matrix.room.RoomAlias;
import io.kamax.matrix.room._RoomAliasLookup;
import io.kamax.matrix.room._RoomCreationOptions;
import java.io.File;
import java.net.URI;
import java.util.List;
import java.util.Optional;
public interface _MatrixClient extends _MatrixClientRaw {
_MatrixID getWhoAmI();
void setDisplayName(String name);
_RoomAliasLookup lookup(RoomAlias alias);
_MatrixRoom createRoom(_RoomCreationOptions options);
_MatrixRoom getRoom(String roomId);
List<_MatrixRoom> getJoinedRooms();
_MatrixRoom joinRoom(String roomIdOrAlias);
_MatrixUser getUser(_MatrixID mxId);
Optional<String> getDeviceId();
/* Custom endpoint! */
// TODO refactor into custom synapse class?
void register(MatrixPasswordCredentials credentials, String sharedSecret, boolean admin);
/***
* Set the access token to use for any authenticated API calls.
*
* @param accessToken
* The access token provided by the server which must be valid
* @throws MatrixClientRequestException
* If an error occurred while checking for the identity behind the access token
*/
void setAccessToken(String accessToken) throws MatrixClientRequestException;
void login(MatrixPasswordCredentials credentials);
void logout();
_SyncData sync(_SyncOptions options);
/**
* Download content from the media repository
*
* @param mxUri
* The MXC URI for the content to download
* @return The content
* @throws IllegalArgumentException
* if the parameter is not a valid MXC URI
*/
_MatrixContent getMedia(String mxUri) throws IllegalArgumentException;
/**
* Download content from the media repository
*
* @param mxUri
* The MXC URI for the content to download
* @return The content
* @throws IllegalArgumentException
* if the parameter is not a valid MXC URI
*/
_MatrixContent getMedia(URI mxUri) throws IllegalArgumentException;
/**
* Upload content to the media repository
*
* @param data
* The data to send
* @param type
* The mime-type of the content upload
* @return The MXC URI for the uploaded content
*/
String putMedia(byte[] data, String type);
/**
* Upload content to the media repository
*
* @param data
* The data to send
* @param type
* The mime-type of the content upload
* @param filename
* A suggested filename for the content
* @return The MXC URI for the uploaded content
*/
String putMedia(byte[] data, String type, String filename);
/**
* Upload content to the media repository
*
* @param data
* The file to read the data from
* @param type
* The mime-type of the content upload
* @return The MXC URI for the uploaded content
*/
String putMedia(File data, String type);
/**
* Upload content to the media repository
*
* @param data
* The data to send
* @param type
* The mime-type of the content upload
* @param filename
* A suggested filename for the content
* @return The MXC URI for the uploaded content
*/
String putMedia(File data, String type, String filename);
List<JsonObject> getPushers();
void setPusher(JsonObject pusher);
void deletePusher(String pushKey);
_GlobalPushRulesSet getPushRules();
_PushRule getPushRule(String scope, String kind, String id);
}

View File

@@ -0,0 +1,51 @@
/*
* 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.client;
import io.kamax.matrix._MatrixID;
import io.kamax.matrix.hs._MatrixHomeserver;
import java.util.List;
import java.util.Optional;
public interface _MatrixClientRaw {
MatrixClientContext getContext();
_MatrixHomeserver getHomeserver();
Optional<String> getAccessToken();
Optional<_MatrixID> getUser();
Optional<_AutoDiscoverySettings> discoverSettings();
// FIXME
// we should maybe have a dedicated object for HS related items and be merged into getHomeserver() which is only
// holding state at this point and is not functional
List<String> getHomeApiVersions();
// FIXME
// we should maybe have a dedicated object for IS related items. Will reconsider when implementing
// other part of the IS API
boolean validateIsBaseUrl();
}

View File

@@ -0,0 +1,29 @@
/*
* matrix-java-sdk - Matrix Client SDK for Java
* 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.matrix.client;
public interface _Presence {
String getStatus();
Long getLastActive();
}

View File

@@ -0,0 +1,43 @@
/*
* matrix-java-sdk - Matrix Client SDK for Java
* Copyright (C) 2018 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.client;
import com.google.gson.JsonObject;
import java.util.List;
public interface _PushRule {
JsonObject getJson();
void set(JsonObject data);
void delete();
boolean isEnabled();
void setEnabled(boolean enabled);
List<String> getActions();
void setActions(List<String> data);
}

View File

@@ -0,0 +1,253 @@
/*
* matrix-java-sdk - Matrix Client SDK for Java
* Copyright (C) 2018 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.client;
import com.google.gson.JsonObject;
import io.kamax.matrix.event._MatrixAccountDataEvent;
import io.kamax.matrix.event._MatrixEphemeralEvent;
import io.kamax.matrix.event._MatrixPersistentEvent;
import io.kamax.matrix.event._MatrixStateEvent;
import java.util.List;
import java.util.Set;
/**
* Representation of the data when performing a sync call on the Matrix Client API.
*/
public interface _SyncData {
interface State {
/**
* The state of the room.
*
* @return a list of state events.
*/
List<_MatrixStateEvent> getEvents();
}
interface Timeline {
/**
* Events that happened in the sync window.
*
* @return List of events.
*/
List<_MatrixPersistentEvent> getEvents();
/**
* If the number of events returned was limited by the sync filter.
*
* @return true if the number of events was limited, false if not.
*/
boolean isLimited();
/**
* Token that can be supplied to fetch previous events for the associated room.
*
* @return the token.
*/
String getPreviousBatchToken();
}
interface Ephemeral {
/**
* Events that happened in the sync window.
*
* @return List of events.
*/
List<_MatrixEphemeralEvent> getEvents();
}
interface AccountData {
/**
* Events that happened in the sync window.
*
* @return List of events.
*/
List<_MatrixAccountDataEvent> getEvents();
}
interface InvitedRoom {
/**
* The ID of the room the user was invited to.
*
* @return the room ID.
*/
String getId();
/**
* The state of the room at the invite event.
*
* @return a list of state events.
*/
State getState();
}
interface UnreadNotifications {
/**
* The number of unread notifications with the highlight flag set.
*
* @return the count.
*/
long getHighlightCount();
/**
* The total number of unread notifications.
*
* @return the count.
*/
long getNotificationCount();
}
interface JoinedRoom {
/**
* The ID of the room the user is joined to.
*
* @return the room id.
*/
String getId();
/**
* State changes prior the start of the timeline.
*
* @return a list of state events.
*/
State getState();
/**
* The room timeline for this sync batch.
*
* @return the timeline.
*/
Timeline getTimeline();
/**
* Ephemeral events of the room.
*
* @return a list of ephemeral events.
*/
Ephemeral getEphemeral();
/**
* Account events of the room.
*
* @return a list of account data events.
*/
AccountData getAccountData();
/**
* The Counts of unread notifications.
*
* @return unread notifications.
*/
UnreadNotifications getUnreadNotifications();
}
interface LeftRoom {
/**
* The ID of the room the user is joined to.
*
* @return the room id.
*/
String getId();
/**
* State changes prior the start of the timeline.
*
* @return a list of state events.
*/
State getState();
/**
* The room timeline up to the leave event.
*
* @return the timeline.
*/
Timeline getTimeline();
}
interface Rooms {
/**
* Rooms the user was invited to within this sync window.
*
* @return Set of InvitedRoom objects.
*/
Set<InvitedRoom> getInvited();
/**
* Rooms the user was joined in within this sync window.
*
* @return Set of JoinedRoom objects.
*/
Set<JoinedRoom> getJoined();
/**
* Rooms the user left from within this sync window.
*
* @return Set of LeftRoom objects.
*/
Set<LeftRoom> getLeft();
}
/**
* The batch token to supply in the next sync call.
*
* @return the batch token.
*/
String nextBatchToken();
/**
* The global private data created by this user.
*
* @return the account data.
*/
AccountData getAccountData();
/**
* Update to the rooms.
*
* @return rooms object.
*/
Rooms getRooms();
/**
* The raw JSON data for this object.
*
* @return the JSON data.
*/
JsonObject getJson();
}

View File

@@ -0,0 +1,65 @@
/*
* matrix-java-sdk - Matrix Client SDK for Java
* Copyright (C) 2018 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.client;
import java.util.Optional;
/**
* Possible options that can be passed to the sync call.
*/
public interface _SyncOptions {
/**
* The point of time to continue the sync from.
*
* @return A token that was provided in a previous sync call, if set.
*/
Optional<String> getSince();
/**
* The filter to use for the sync.
*
* @return The ID or raw JSON filter, if set.
*/
Optional<String> getFilter();
/**
* If the full state should be included in the sync.
*
* @return The full state option, if set.
*/
Optional<Boolean> withFullState();
/**
* If the client should automatically be marked as online.
*
* @return The set presence option, if set.
*/
Optional<String> getSetPresence();
/**
* The maximum time to wait, in milliseconds, before ending the sync call.
*
* @return The timeout option, if set.
*/
Optional<Long> getTimeout();
}

View File

@@ -0,0 +1,87 @@
/*
* 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.client.as;
import io.kamax.matrix.client.MatrixClientContext;
import io.kamax.matrix.client.MatrixClientDefaults;
import io.kamax.matrix.client._MatrixClient;
import io.kamax.matrix.client.regular.MatrixHttpClient;
import io.kamax.matrix.json.VirtualUserRegistrationBody;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.URL;
import okhttp3.OkHttpClient;
import okhttp3.Request;
public class MatrixApplicationServiceClient extends MatrixHttpClient implements _MatrixApplicationServiceClient {
private Logger log = LoggerFactory.getLogger(MatrixApplicationServiceClient.class);
public MatrixApplicationServiceClient(String domain) {
super(domain);
}
public MatrixApplicationServiceClient(URL hsBaseUrl) {
super(hsBaseUrl);
}
public MatrixApplicationServiceClient(MatrixClientContext context) {
super(context);
}
public MatrixApplicationServiceClient(MatrixClientContext context, OkHttpClient.Builder client) {
super(context, client);
}
public MatrixApplicationServiceClient(MatrixClientContext context, OkHttpClient.Builder client,
MatrixClientDefaults defaults) {
super(context, client, defaults);
}
public MatrixApplicationServiceClient(MatrixClientContext context, OkHttpClient client) {
super(context, client);
}
private MatrixHttpClient createClient(String localpart) {
MatrixClientContext context = new MatrixClientContext(getContext()).setUserWithLocalpart(localpart)
.setVirtual(true);
return new MatrixHttpClient(context);
}
@Override
public _MatrixClient createUser(String localpart) {
log.debug("Creating new user {}", localpart);
URL path = getClientPath("register");
executeAuthenticated(
new Request.Builder().post(getJsonBody(new VirtualUserRegistrationBody(localpart))).url(path));
return createClient(localpart);
}
@Override
public _MatrixClient getUser(String localpart) {
return createClient(localpart);
}
}

View File

@@ -0,0 +1,31 @@
/*
* 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.client.as;
import io.kamax.matrix.client._MatrixClient;
public interface _MatrixApplicationServiceClient extends _MatrixClient {
_MatrixClient createUser(String localpart);
_MatrixClient getUser(String localpart);
}

View File

@@ -0,0 +1,68 @@
/*
* matrix-java-sdk - Matrix Client SDK for Java
* Copyright (C) 2018 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.client.regular;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import io.kamax.matrix.client._GlobalPushRulesSet;
import io.kamax.matrix.json.GsonUtil;
import java.util.List;
public class GlobalPushRulesSet implements _GlobalPushRulesSet {
private JsonObject data;
public GlobalPushRulesSet(JsonObject data) {
this.data = data;
}
private List<JsonObject> getForKey(String key) {
return GsonUtil.asList(GsonUtil.findArray(data, key).orElseGet(JsonArray::new), JsonObject.class);
}
@Override
public List<JsonObject> getContent() {
return getForKey("content");
}
@Override
public List<JsonObject> getOverride() {
return getForKey("override");
}
@Override
public List<JsonObject> getRoom() {
return getForKey("room");
}
@Override
public List<JsonObject> getSender() {
return getForKey("sender");
}
@Override
public List<JsonObject> getUnderride() {
return getForKey("underride");
}
}

View File

@@ -0,0 +1,292 @@
/*
* 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.client.regular;
import com.google.gson.JsonNull;
import com.google.gson.JsonObject;
import io.kamax.matrix.MatrixID;
import io.kamax.matrix._MatrixContent;
import io.kamax.matrix._MatrixID;
import io.kamax.matrix._MatrixUser;
import io.kamax.matrix.client.*;
import io.kamax.matrix.hs._MatrixRoom;
import io.kamax.matrix.json.*;
import io.kamax.matrix.room.RoomAlias;
import io.kamax.matrix.room.RoomAliasLookup;
import io.kamax.matrix.room._RoomAliasLookup;
import io.kamax.matrix.room._RoomCreationOptions;
import org.apache.commons.codec.digest.HmacAlgorithms;
import org.apache.commons.codec.digest.HmacUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.net.URI;
import java.net.URL;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import okhttp3.*;
public class MatrixHttpClient extends AMatrixHttpClient implements _MatrixClient {
private Logger log = LoggerFactory.getLogger(MatrixHttpClient.class);
public MatrixHttpClient(String domain) {
super(domain);
}
public MatrixHttpClient(URL hsBaseUrl) {
super(hsBaseUrl);
}
public MatrixHttpClient(MatrixClientContext context) {
super(context);
}
public MatrixHttpClient(MatrixClientContext context, OkHttpClient.Builder client) {
super(context, client);
}
public MatrixHttpClient(MatrixClientContext context, OkHttpClient.Builder client, MatrixClientDefaults defaults) {
super(context, client, defaults);
}
public MatrixHttpClient(MatrixClientContext context, OkHttpClient client) {
super(context, client);
}
protected _MatrixID getIdentity(String token) {
URL path = getClientPath("account", "whoami");
String body = executeAuthenticated(new Request.Builder().get().url(path), token);
return MatrixID.from(GsonUtil.getStringOrThrow(GsonUtil.parseObj(body), "user_id")).acceptable();
}
@Override
public _MatrixID getWhoAmI() {
URL path = getClientPath("account", "whoami");
String body = executeAuthenticated(new Request.Builder().get().url(path));
return MatrixID.from(GsonUtil.getStringOrThrow(GsonUtil.parseObj(body), "user_id")).acceptable();
}
@Override
public void setDisplayName(String name) {
URL path = getClientPath("profile", getUserId(), "displayname");
execute(new Request.Builder().put(getJsonBody(new UserDisplaynameSetBody(name))).url(path));
}
@Override
public _RoomAliasLookup lookup(RoomAlias alias) {
URL path = getClientPath("directory", "room", alias.getId());
String resBody = execute(new Request.Builder().get().url(path));
RoomAliasLookupJson lookup = GsonUtil.get().fromJson(resBody, RoomAliasLookupJson.class);
return new RoomAliasLookup(lookup.getRoomId(), alias.getId(), lookup.getServers());
}
@Override
public _MatrixRoom createRoom(_RoomCreationOptions options) {
URL path = getClientPath("createRoom");
String resBody = executeAuthenticated(
new Request.Builder().post(getJsonBody(new RoomCreationRequestJson(options))).url(path));
String roomId = GsonUtil.get().fromJson(resBody, RoomCreationResponseJson.class).getRoomId();
return getRoom(roomId);
}
@Override
public _MatrixRoom getRoom(String roomId) {
return new MatrixHttpRoom(getContext(), roomId);
}
@Override
public List<_MatrixRoom> getJoinedRooms() {
URL path = getClientPath("joined_rooms");
JsonObject resBody = GsonUtil.parseObj(executeAuthenticated(new Request.Builder().get().url(path)));
return GsonUtil.asList(resBody, "joined_rooms", String.class).stream().map(this::getRoom)
.collect(Collectors.toList());
}
@Override
public _MatrixRoom joinRoom(String roomIdOrAlias) {
URL path = getClientPath("join", roomIdOrAlias);
String resBody = executeAuthenticated(new Request.Builder().post(getJsonBody(new JsonObject())).url(path));
String roomId = GsonUtil.get().fromJson(resBody, RoomCreationResponseJson.class).getRoomId();
return getRoom(roomId);
}
@Override
public _MatrixUser getUser(_MatrixID mxId) {
return new MatrixHttpUser(getContext(), mxId);
}
@Override
public Optional<String> getDeviceId() {
return Optional.ofNullable(context.getDeviceId());
}
protected void updateContext(String resBody) {
LoginResponse response = gson.fromJson(resBody, LoginResponse.class);
context.setToken(response.getAccessToken());
context.setDeviceId(response.getDeviceId());
context.setUser(MatrixID.asAcceptable(response.getUserId()));
}
@Override
public void register(MatrixPasswordCredentials credentials, String sharedSecret, boolean admin) {
// As per synapse registration script:
// https://github.com/matrix-org/synapse/blob/master/scripts/register_new_matrix_user#L28
String value = credentials.getLocalPart() + "\0" + credentials.getPassword() + "\0"
+ (admin ? "admin" : "notadmin");
String mac = new HmacUtils(HmacAlgorithms.HMAC_SHA_1, sharedSecret).hmacHex(value);
JsonObject body = new JsonObject();
body.addProperty("user", credentials.getLocalPart());
body.addProperty("password", credentials.getPassword());
body.addProperty("mac", mac);
body.addProperty("type", "org.matrix.login.shared_secret");
body.addProperty("admin", false);
URL url = getPath("client", "api", "v1", "register");
updateContext(execute(new Request.Builder().post(getJsonBody(body)).url(url)));
}
@Override
public void setAccessToken(String accessToken) {
context.setUser(getIdentity(accessToken));
context.setToken(accessToken);
}
@Override
public void login(MatrixPasswordCredentials credentials) {
URL url = getClientPath("login");
LoginPostBody data = new LoginPostBody(credentials.getLocalPart(), credentials.getPassword());
getDeviceId().ifPresent(data::setDeviceId);
Optional.ofNullable(context.getInitialDeviceName()).ifPresent(data::setInitialDeviceDisplayName);
updateContext(execute(new Request.Builder().post(getJsonBody(data)).url(url)));
}
@Override
public void logout() {
URL path = getClientPath("logout");
executeAuthenticated(new Request.Builder().post(getJsonBody(new JsonObject())).url(path));
context.setToken(null);
context.setUser(null);
context.setDeviceId(null);
}
@Override
public _SyncData sync(_SyncOptions options) {
long start = System.currentTimeMillis();
HttpUrl.Builder path = getClientPathBuilder("sync");
path.addQueryParameter("timeout", options.getTimeout().map(Long::intValue).orElse(30000).toString());
options.getSince().ifPresent(since -> path.addQueryParameter("since", since));
options.getFilter().ifPresent(filter -> path.addQueryParameter("filter", filter));
options.withFullState().ifPresent(state -> path.addQueryParameter("full_state", state ? "true" : "false"));
options.getSetPresence().ifPresent(presence -> path.addQueryParameter("presence", presence));
String body = executeAuthenticated(new Request.Builder().get().url(path.build().url()));
long request = System.currentTimeMillis();
log.info("Sync: network request took {} ms", (request - start));
SyncDataJson data = new SyncDataJson(GsonUtil.parseObj(body));
long parsing = System.currentTimeMillis();
log.info("Sync: parsing took {} ms", (parsing - request));
return data;
}
@Override
public _MatrixContent getMedia(String mxUri) throws IllegalArgumentException {
return getMedia(URI.create(mxUri));
}
@Override
public _MatrixContent getMedia(URI mxUri) throws IllegalArgumentException {
return new MatrixHttpContent(context, mxUri);
}
private String putMedia(Request.Builder builder, String filename) {
HttpUrl.Builder b = getMediaPathBuilder("upload");
if (StringUtils.isNotEmpty(filename)) b.addQueryParameter("filename", filename);
String body = executeAuthenticated(builder.url(b.build()));
return GsonUtil.getStringOrThrow(GsonUtil.parseObj(body), "content_uri");
}
@Override
public String putMedia(byte[] data, String type) {
return putMedia(data, type, null);
}
@Override
public String putMedia(byte[] data, String type, String filename) {
return putMedia(new Request.Builder().post(RequestBody.create(MediaType.parse(type), data)), filename);
}
@Override
public String putMedia(File data, String type) {
return putMedia(data, type, null);
}
@Override
public String putMedia(File data, String type, String filename) {
return putMedia(new Request.Builder().post(RequestBody.create(MediaType.parse(type), data)), filename);
}
@Override
public List<JsonObject> getPushers() {
URL url = getClientPath("pushers");
JsonObject response = GsonUtil.parseObj(executeAuthenticated(new Request.Builder().get().url(url)));
return GsonUtil.findArray(response, "pushers").map(array -> GsonUtil.asList(array, JsonObject.class))
.orElse(Collections.emptyList());
}
@Override
public void setPusher(JsonObject pusher) {
URL url = getClientPath("pushers", "set");
executeAuthenticated(new Request.Builder().url(url).post(getJsonBody(pusher)));
}
@Override
public void deletePusher(String pushKey) {
JsonObject pusher = new JsonObject();
pusher.add("kind", JsonNull.INSTANCE);
pusher.addProperty("pushkey", pushKey);
setPusher(pusher);
}
@Override
public _GlobalPushRulesSet getPushRules() {
URL url = getClientPath("pushrules", "global", "");
JsonObject response = GsonUtil.parseObj(executeAuthenticated(new Request.Builder().url(url).get()));
return new GlobalPushRulesSet(response);
}
@Override
public _PushRule getPushRule(String scope, String kind, String id) {
return new MatrixHttpPushRule(context, scope, kind, id);
}
}

View File

@@ -0,0 +1,48 @@
/*
* matrix-java-sdk - Matrix Client SDK for Java
* 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.matrix.client.regular;
import com.google.gson.JsonObject;
import io.kamax.matrix.client._Presence;
import io.kamax.matrix.json.GsonUtil;
public class Presence implements _Presence {
private String status;
private Long lastActive;
public Presence(JsonObject json) {
this.status = GsonUtil.getStringOrThrow(json, "presence");
this.lastActive = GsonUtil.getLong(json, "last_active_ago");
}
@Override
public String getStatus() {
return status;
}
@Override
public Long getLastActive() {
return lastActive;
}
}

View File

@@ -0,0 +1,390 @@
/*
* matrix-java-sdk - Matrix Client SDK for Java
* Copyright (C) 2018 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.client.regular;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import io.kamax.matrix.MatrixID;
import io.kamax.matrix._MatrixID;
import io.kamax.matrix.client._SyncData;
import io.kamax.matrix.event.EventKey;
import io.kamax.matrix.event._MatrixAccountDataEvent;
import io.kamax.matrix.event._MatrixEphemeralEvent;
import io.kamax.matrix.event._MatrixPersistentEvent;
import io.kamax.matrix.event._MatrixStateEvent;
import io.kamax.matrix.json.MatrixJsonObject;
import java.util.*;
public class SyncDataJson extends MatrixJsonObject implements _SyncData {
public class MatrixPersistentEventJson extends MatrixJsonObject implements _MatrixPersistentEvent {
public MatrixPersistentEventJson(JsonObject obj) {
super(obj);
}
@Override
public String getId() {
return findString(EventKey.Id.get()).orElse(""); // FIXME refactor event structure
}
@Override
public String getType() {
return getString(EventKey.Type.get());
}
@Override
public Long getTime() {
return getLong(EventKey.Timestamp.get());
}
@Override
public _MatrixID getSender() {
return MatrixID.from(getString(EventKey.Sender.get())).acceptable();
}
}
public class MatrixEphemeralEventJson extends MatrixJsonObject implements _MatrixEphemeralEvent {
public MatrixEphemeralEventJson(JsonObject obj) {
super(obj);
}
@Override
public String getType() {
return getString(EventKey.Type.get());
}
}
public class MatrixAccountDataEventJson extends MatrixJsonObject implements _MatrixAccountDataEvent {
public MatrixAccountDataEventJson(JsonObject obj) {
super(obj);
}
@Override
public String getType() {
return getString(EventKey.Type.get());
}
}
public class MatrixStateEventJson extends MatrixPersistentEventJson implements _MatrixStateEvent {
public MatrixStateEventJson(JsonObject obj) {
super(obj);
}
@Override
public String getStateKey() {
return getString(EventKey.StateKey.get());
}
}
public class StateJson extends MatrixJsonObject implements _SyncData.State {
private List<_MatrixStateEvent> events = new ArrayList<>();
public StateJson(JsonObject obj) {
super(obj);
findArray("events").ifPresent(array -> {
for (JsonElement el : array) {
events.add(new MatrixStateEventJson(asObj(el)));
}
});
}
@Override
public List<_MatrixStateEvent> getEvents() {
return events;
}
}
public class TimelineJson extends MatrixJsonObject implements _SyncData.Timeline {
private List<_MatrixPersistentEvent> events = new ArrayList<>();
public TimelineJson(JsonObject obj) {
super(obj);
findArray("events").ifPresent(array -> {
for (JsonElement el : array) {
events.add(new MatrixPersistentEventJson(asObj(el)));
}
});
}
@Override
public List<_MatrixPersistentEvent> getEvents() {
return events;
}
@Override
public boolean isLimited() {
return findString("limited").map("true"::equals).orElse(false);
}
@Override
public String getPreviousBatchToken() {
return getString("prev_batch");
}
}
public class EphemeralJson extends MatrixJsonObject implements _SyncData.Ephemeral {
private List<_MatrixEphemeralEvent> events = new ArrayList<>();
public EphemeralJson(JsonObject obj) {
super(obj);
findArray("events").ifPresent(array -> {
for (JsonElement el : array) {
events.add(new MatrixEphemeralEventJson(asObj(el)));
}
});
}
@Override
public List<_MatrixEphemeralEvent> getEvents() {
return events;
}
}
public class AccountDataJson extends MatrixJsonObject implements _SyncData.AccountData {
private List<_MatrixAccountDataEvent> events = new ArrayList<>();
public AccountDataJson(JsonObject obj) {
super(obj);
findArray("events").ifPresent(array -> {
for (JsonElement el : array) {
events.add(new MatrixAccountDataEventJson(asObj(el)));
}
});
}
@Override
public List<_MatrixAccountDataEvent> getEvents() {
return events;
}
}
public class InvitedRoomJson extends MatrixJsonObject implements _SyncData.InvitedRoom {
private String id;
private State state;
public InvitedRoomJson(String id, JsonObject data) {
super(data);
this.id = id;
this.state = new StateJson(findObj("invite_state").orElseGet(JsonObject::new));
}
@Override
public String getId() {
return id;
}
@Override
public State getState() {
return state;
}
}
public class UnreadNotificationsJson extends MatrixJsonObject implements _SyncData.UnreadNotifications {
private long highlights;
private long global;
public UnreadNotificationsJson(JsonObject data) {
super(data);
this.highlights = findLong("highlight_count").orElse(0L);
this.global = findLong("notification_count").orElse(0L);
}
@Override
public long getHighlightCount() {
return highlights;
}
@Override
public long getNotificationCount() {
return global;
}
}
public class JoinedRoomJson extends MatrixJsonObject implements _SyncData.JoinedRoom {
private String id;
private State state;
private Timeline timeline;
private UnreadNotifications unreadNotifications;
private Ephemeral ephemeral;
private AccountData accountData;
public JoinedRoomJson(String id, JsonObject data) {
super(data);
this.id = id;
this.state = new StateJson(findObj("state").orElseGet(JsonObject::new));
this.timeline = new TimelineJson(findObj("timeline").orElseGet(JsonObject::new));
this.unreadNotifications = new UnreadNotificationsJson(computeObj("unread_notifications"));
this.ephemeral = new EphemeralJson(findObj("ephemeral").orElseGet(JsonObject::new));
this.accountData = new AccountDataJson(findObj("account_data").orElseGet(JsonObject::new));
}
@Override
public String getId() {
return id;
}
@Override
public State getState() {
return state;
}
@Override
public Timeline getTimeline() {
return timeline;
}
@Override
public Ephemeral getEphemeral() {
return ephemeral;
}
@Override
public AccountData getAccountData() {
return accountData;
}
@Override
public UnreadNotifications getUnreadNotifications() {
return unreadNotifications;
}
}
public class LeftRoomJson extends MatrixPersistentEventJson implements _SyncData.LeftRoom {
private String id;
private State state;
private Timeline timeline;
public LeftRoomJson(String id, JsonObject data) {
super(data);
this.id = id;
this.state = new StateJson(findObj("state").orElseGet(JsonObject::new));
this.timeline = new TimelineJson(findObj("timeline").orElseGet(JsonObject::new));
}
@Override
public String getId() {
return id;
}
@Override
public State getState() {
return state;
}
@Override
public Timeline getTimeline() {
return timeline;
}
}
public class RoomsJson extends MatrixJsonObject implements _SyncData.Rooms {
private Set<InvitedRoom> invited = new HashSet<>();
private Set<JoinedRoom> joined = new HashSet<>();
private Set<LeftRoom> left = new HashSet<>();
public RoomsJson(JsonObject obj) {
super(obj);
findObj("invite").ifPresent(o -> {
for (Map.Entry<String, JsonElement> entry : o.entrySet()) {
invited.add(new InvitedRoomJson(entry.getKey(), asObj(entry.getValue())));
}
});
findObj("join").ifPresent(o -> {
for (Map.Entry<String, JsonElement> entry : o.entrySet()) {
joined.add(new JoinedRoomJson(entry.getKey(), asObj(entry.getValue())));
}
});
findObj("leave").ifPresent(o -> {
for (Map.Entry<String, JsonElement> entry : o.entrySet()) {
left.add(new LeftRoomJson(entry.getKey(), asObj(entry.getValue())));
}
});
}
@Override
public Set<InvitedRoom> getInvited() {
return invited;
}
@Override
public Set<JoinedRoom> getJoined() {
return joined;
}
@Override
public Set<LeftRoom> getLeft() {
return left;
}
}
private String nextBatch;
private AccountDataJson accountData;
private RoomsJson rooms;
public SyncDataJson(JsonObject data) {
super(data);
nextBatch = getString("next_batch");
accountData = new AccountDataJson(findObj("account_data").orElseGet(JsonObject::new));
rooms = new RoomsJson(findObj("rooms").orElseGet(JsonObject::new));
}
@Override
public String nextBatchToken() {
return nextBatch;
}
@Override
public AccountData getAccountData() {
return accountData;
}
@Override
public Rooms getRooms() {
return rooms;
}
}

View File

@@ -0,0 +1,103 @@
/*
* matrix-java-sdk - Matrix Client SDK for Java
* Copyright (C) 2018 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.client.regular;
import io.kamax.matrix.client._SyncOptions;
import java.util.Optional;
public class SyncOptions implements _SyncOptions {
public static class Builder {
private final SyncOptions obj;
public Builder() {
this.obj = new SyncOptions();
}
public SyncOptions get() {
return obj;
}
public Builder setSince(String since) {
obj.since = since;
return this;
}
public Builder setFilter(String filter) {
obj.filter = filter;
return this;
}
public Builder setWithFullState(boolean withFullState) {
obj.fullState = withFullState;
return this;
}
public Builder setPresence(boolean setPresence) {
obj.setPresence = setPresence ? null : "offline";
return this;
}
public Builder setTimeout(long timeout) {
obj.timeout = timeout;
return this;
}
}
public static Builder build() {
return new Builder();
}
private String since;
private String filter;
private Boolean fullState;
private String setPresence;
private Long timeout;
@Override
public Optional<String> getSince() {
return Optional.ofNullable(since);
}
@Override
public Optional<String> getFilter() {
return Optional.ofNullable(filter);
}
@Override
public Optional<Boolean> withFullState() {
return Optional.ofNullable(fullState);
}
@Override
public Optional<String> getSetPresence() {
return Optional.ofNullable(setPresence);
}
@Override
public Optional<Long> getTimeout() {
return Optional.ofNullable(timeout);
}
}

View File

@@ -0,0 +1,36 @@
/*
* 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.util.Base64;
public class MxBase64 {
public static String encode(byte[] data) {
return Base64.getEncoder().withoutPadding().encodeToString(data);
}
public static String encode(String data) {
return encode(data.getBytes(StandardCharsets.UTF_8));
}
}

View File

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

@@ -0,0 +1,80 @@
/*
* 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.crypto;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
public class KeyFileStore implements _KeyStore {
private final Charset charset = StandardCharsets.UTF_8;
private File file;
public KeyFileStore(String path) {
File file = new File(path);
if (!file.exists()) {
throw new IllegalArgumentException("Signing key file storage " + path + " does not exist");
}
if (file.isDirectory()) {
throw new IllegalArgumentException("Signing key file storage " + path + " is a directory");
}
if (!file.isFile()) {
throw new IllegalArgumentException("Signing key file storage " + path + " is not a regular file");
}
if (!file.canRead()) {
throw new IllegalArgumentException("Signing key file storage " + path + " is not readable");
}
this.file = file;
}
@Override
public Optional<String> load() {
try {
List<String> keys = FileUtils.readLines(file, charset);
return keys.stream().filter(StringUtils::isNotBlank).findFirst();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public void store(String key) {
try {
FileUtils.writeLines(file, charset.name(), Collections.singletonList(key), false);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

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