Compare commits
	
		
			185 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | d5680b2dfe | ||
|  | 5aad4fb81e | ||
|  | a1f64f5159 | ||
|  | a96920f533 | ||
|  | 9ca2ebdad0 | ||
|  | 7f284ac71e | ||
|  | 4d488a3fc8 | ||
|  | 7ee3e19de4 | ||
|  | 608938c084 | ||
|  | e6fec9199d | ||
|  | c3262a9f25 | ||
|  | 136563c61a | ||
|  | 3043cd4e61 | ||
|  | 21d9d0fda1 | ||
|  | a964b073bf | ||
|  | f85345bc97 | ||
|  | 29603682e5 | ||
|  | d54f1dcb88 | ||
|  | 92f10347d1 | ||
|  | 0298f66212 | ||
|  | 0ddd086bda | ||
|  | 544f8e59f0 | ||
|  | 917f87bf8c | ||
|  | 774795c203 | ||
|  | 27b2976e42 | ||
|  | f16f184253 | ||
|  | cd890d114a | ||
|  | 321ba1e325 | ||
|  | c3ce0a17f6 | ||
|  | 0fcc0d9bb2 | ||
|  | ce7f900543 | ||
|  | c7c009f9af | ||
|  | 3b01663245 | ||
|  | 9cc601d582 | ||
|  | e6272b1827 | ||
|  | 8243354f39 | ||
|  | 25968e0737 | ||
|  | 44a80461a0 | ||
|  | 85d9f9e704 | ||
|  | 6278301672 | ||
|  | 5ed0c66cfd | ||
|  | ea58b6985a | ||
|  | a44f781495 | ||
|  | 0d42ee695a | ||
|  | f331af0941 | ||
|  | e2c8a56135 | ||
|  | a67c5d7ae1 | ||
|  | 80352070f1 | ||
|  | 39447b8b8b | ||
|  | 9d4680f55a | ||
|  | d1ea0fbf0f | ||
|  | ee21f051fb | ||
|  | 6cc17abf2c | ||
|  | a7b5accd75 | ||
|  | 6bb0c93f57 | ||
|  | 9abdcc15ba | ||
|  | eb903bf226 | ||
|  | 1cbb0a135b | ||
|  | 1587103c0a | ||
|  | 838d79ae15 | ||
|  | 96c47ecf76 | ||
|  | c5cea933a4 | ||
|  | 57c7e4a91d | ||
|  | 1dce59a02e | ||
|  | de840b9d00 | ||
|  | 53c85d2248 | ||
|  | 254dc5684f | ||
|  | de92e98f7d | ||
|  | d5f9137056 | ||
|  | 1307e3aa43 | ||
|  | dfedde0df6 | ||
|  | 93bd7354c2 | ||
|  | c302789898 | ||
|  | 96155c1876 | ||
|  | 95ee328281 | ||
|  | 72a1794cc3 | ||
|  | 37ddd0e588 | ||
|  | 4d63bba251 | ||
|  | aadfae2965 | ||
|  | 2f7e5e4025 | ||
|  | 77dc75d383 | ||
|  | f3b528d1ba | ||
|  | 91e5e08e70 | ||
|  | acd8c7d7c5 | ||
|  | 249cc0ea92 | ||
|  | 99697d7c75 | ||
|  | e133e120d7 | ||
|  | e39d6bfa10 | ||
|  | 217bc423ed | ||
|  | 8f0654c34e | ||
|  | 8afdb3ed83 | ||
|  | bd4ccbc5e5 | ||
|  | 6d1c6ed109 | ||
|  | 1619f5311c | ||
|  | 6fa36ea092 | ||
|  | 471e06536b | ||
|  | 3a6b75996c | ||
|  | 566e4f3137 | ||
|  | a4c18dee5d | ||
|  | 8d6850d346 | ||
|  | 67bc18af7d | ||
|  | 5c660fdcaf | ||
|  | fbbafeb769 | ||
|  | 559f6a7401 | ||
|  | 3bebb33147 | ||
|  | 3e240fe34d | ||
|  | 635f6fdbe7 | ||
|  | 4237eeb3b6 | ||
|  | a0e91e7896 | ||
|  | aab0b86646 | ||
|  | 3e22301af7 | ||
|  | 2b202323c0 | ||
|  | 4ec05f518e | ||
|  | 6da68298b0 | ||
|  | aecaafdeca | ||
|  | d885932f45 | ||
|  | c689a3f161 | ||
|  | 7805112548 | ||
|  | 3e89f0bc5e | ||
|  | c6b8f7d48e | ||
|  | 83377ebee0 | ||
|  | 2aa6e4d142 | ||
|  | 82a1a3df68 | ||
|  | 7ec11ba8cf | ||
|  | 9317c11434 | ||
|  | b257a0275f | ||
|  | 2aaa04062f | ||
|  | 54c3014568 | ||
|  | c3ca73f576 | ||
|  | 4185b644b7 | ||
|  | ace5918342 | ||
|  | 7ad985fead | ||
|  | 6a376db322 | ||
|  | 950f7c931c | ||
|  | d160a44509 | ||
|  | 05493da27c | ||
|  | df44428a85 | ||
|  | e6f9c30611 | ||
|  | 06b2c787d3 | ||
|  | 5645f69208 | ||
|  | 92cf5c6b21 | ||
|  | ad1b91f370 | ||
|  | e9c29f1c03 | ||
|  | f13748abeb | ||
|  | 7208c7e456 | ||
|  | 8857f636d6 | ||
|  | d9fc41e8c7 | ||
|  | da08e0b4ad | ||
|  | 11fc8f08b0 | ||
|  | af4d734105 | ||
|  | 0f4f5ac81b | ||
|  | 8c4ddd2e65 | ||
|  | cb8049b54a | ||
|  | 99b7d9f27d | ||
|  | ded5e3db5e | ||
|  | b892d19023 | ||
|  | 026a2e82d9 | ||
|  | b881f73798 | ||
|  | 99d793b5ed | ||
|  | cb02f62b9d | ||
|  | bd9161ec9b | ||
|  | 544cab816c | ||
|  | cdb56aec1f | ||
|  | 407138e972 | ||
|  | 3eee4eaccf | ||
|  | b3aefbed77 | ||
|  | 29017fbe1e | ||
|  | 843fa04f19 | ||
|  | f7d1a300f1 | ||
|  | f16eb264be | ||
|  | f29014be1f | ||
|  | 20a4d8dd91 | ||
|  | 0c0feab0c0 | ||
|  | dd313881db | ||
|  | feb37112b2 | ||
|  | 1ab8a27fda | ||
|  | deafc420a5 | ||
|  | fce15f0e29 | ||
|  | 5b5893f407 | ||
|  | f55d5fbc80 | ||
|  | b613415dc4 | ||
|  | 0549d23d21 | ||
|  | b493ccd479 | ||
|  | 03e72ba155 | ||
|  | 32a3444a9e | 
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -7,7 +7,8 @@ out/ | ||||
| .idea/ | ||||
|  | ||||
| # Local dev config | ||||
| /ma1sd.yaml | ||||
| /application.yaml | ||||
|  | ||||
| # Local dev storage | ||||
| /mxisd.db | ||||
| /ma1sd.db | ||||
|   | ||||
							
								
								
									
										13
									
								
								Dockerfile
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								Dockerfile
									
									
									
									
									
								
							| @@ -2,16 +2,17 @@ FROM openjdk:8-jre-alpine | ||||
|  | ||||
| RUN apk update && apk add bash && rm -rf /var/lib/apk/* /var/cache/apk/* | ||||
|  | ||||
| VOLUME /etc/mxisd | ||||
| VOLUME /var/mxisd | ||||
| VOLUME /etc/ma1sd | ||||
| VOLUME /var/ma1sd | ||||
| EXPOSE 8090 | ||||
|  | ||||
| ENV JAVA_OPTS="" | ||||
| ENV CONF_FILE_PATH="/etc/mxisd/mxisd.yaml" | ||||
| ENV SIGN_KEY_PATH="/var/mxisd/sign.key" | ||||
| ENV SQLITE_DATABASE_PATH="/var/mxisd/mxisd.db" | ||||
| ENV CONF_FILE_PATH="/etc/ma1sd/ma1sd.yaml" | ||||
| ENV SIGN_KEY_PATH="/var/ma1sd/sign.key" | ||||
| ENV SQLITE_DATABASE_PATH="/var/ma1sd/ma1sd.db" | ||||
|  | ||||
| CMD [ "/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 | ||||
|   | ||||
							
								
								
									
										71
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										71
									
								
								README.md
									
									
									
									
									
								
							| @@ -1,6 +1,6 @@ | ||||
| mxisd - Federated Matrix Identity Server | ||||
| ma1sd - Federated Matrix Identity Server | ||||
| ---------------------------------------- | ||||
|    | ||||
|    | ||||
|  | ||||
| - [Overview](#overview) | ||||
| - [Features](#features) | ||||
| @@ -8,20 +8,29 @@ mxisd - Federated Matrix Identity Server | ||||
| - [Getting Started](#getting-started) | ||||
| - [Support](#support) | ||||
| - [Contribute](#contribute) | ||||
| - [Powered by ma1sd](#powered-by-ma1sd) | ||||
| - [FAQ](#faq) | ||||
| - [Migration from mxisd](#migration-from-mxisd) | ||||
| - [Contact](#contact) | ||||
|  | ||||
| --- | ||||
|  | ||||
| * This project is a fork of the https://github.com/kamax-matrix/mxisd which has been archived and no longer supported. * | ||||
|  | ||||
| --- | ||||
|  | ||||
| # Overview | ||||
| mxisd 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) | ||||
| ma1sd is a Federated Matrix Identity server for self-hosted Matrix infrastructures with [enhanced features](#features). | ||||
| 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. | ||||
| It is the one stop shop for anything regarding Authentication, Directory and Identity management in Matrix built in a | ||||
| 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.   | ||||
| 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: | ||||
| - Email address | ||||
| - 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)**.   | ||||
|  | ||||
| # 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 | ||||
|   ([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.) | ||||
|   ([Spec](https://kamax.io/matrix/api/identity_service/unstable.html#post-matrix-identity-api-v1-store-invite)) | ||||
| - Allow users to add 3PIDs to their settings/profile | ||||
|   ([Spec](https://kamax.io/matrix/api/identity_service/unstable.html#establishing-associations)) | ||||
|   ([Spec](https://matrix.org/docs/spec/identity_service/r0.2.0.html#invitation-storage)) | ||||
| - Allow users to add/remove 3PIDs to their settings/profile via 3PID sessions | ||||
|   ([Spec](https://matrix.org/docs/spec/identity_service/r0.2.0.html#establishing-associations)) | ||||
| - 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: | ||||
| - [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 | ||||
| - [Session Control](docs/threepids/session/session.md): Extensive control of where 3PIDs are transmitted so they are not | ||||
|   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) | ||||
|   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, | ||||
| @@ -66,40 +76,43 @@ As an enhanced Identity service: | ||||
| - 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 | ||||
|  | ||||
| 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 | ||||
| See the [dedicated document](docs/getting-started.md) | ||||
|  | ||||
| # Support | ||||
| ## Community | ||||
| Over Matrix: [#mxisd:kamax.io](https://matrix.to/#/#mxisd:kamax.io) ([Preview](https://view.matrix.org/room/!NPRUEisLjcaMtHIzDr:kamax.io/)) | ||||
| ## Troubleshooting | ||||
| 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 | ||||
| If you would prefer professional support/custom development for mxisd and/or for Matrix in general, including other open | ||||
| source technologies/products: | ||||
| - Visit our [website](https://www.kamax.io/) to get in touch with us and get a quote. | ||||
| - Come in our general Matrix room: [#kamax-matrix:kamax.io](https://matrix.to/#/#kamax-matrix:kamax.io) | ||||
| Sorry, I cannot provide commercial support (at least now). But always try to help you. | ||||
|  | ||||
| Don't hesitate to ask about project and feel free to create issues at https://github.com/ma1uta/ma1sd | ||||
|  | ||||
| # Contribute  | ||||
| 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! | ||||
| - Opening issues for any weird behaviour or bug. mxisd should feel natural, let us know if it does not! | ||||
| - 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. 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 | ||||
| changes you feel improve the doc. | ||||
| - 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: | ||||
| - Get a [support contract](#commercial). This is the best way you can help us as it ensures mxisd is | ||||
| maintained regularly and you get direct access to the support team. | ||||
| - Sponsoring new features or bug fixes. [Get in touch](#contact) so we can discuss it further. | ||||
| # Powered by ma1sd | ||||
| The following projects can use ma1sd under the hood for some or all their features. Check them out! | ||||
| - [matrix-docker-ansible-deploy](https://github.com/spantaleev/matrix-docker-ansible-deploy) | ||||
| - [matrix-register-bot](https://github.com/krombel/matrix-register-bot) | ||||
|  | ||||
| # FAQ | ||||
| See the [dedicated document](docs/faq.md) | ||||
|  | ||||
| # Migration from mxisd | ||||
|  | ||||
| See the [migration guide](docs/migration-from-mxisd.md) | ||||
|  | ||||
| # Contact | ||||
| Get in touch via: | ||||
| - Matrix: [#mxisd:kamax.io](https://matrix.to/#/#mxisd:kamax.io) | ||||
| - Email: see our website: [Kamax.io](https://www.kamax.io) | ||||
| - Matrix: [#ma1sd:ru-matrix.org](https://matrix.to/#/#ma1sd:ru-matrix.org) | ||||
|   | ||||
| @@ -1,99 +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-io/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' | ||||
|  | ||||
|  | ||||
| ################ | ||||
| # LDAP Backend # | ||||
| ################ | ||||
| # If you would like to integrate with your AD/Samba/LDAP server, | ||||
| # see https://github.com/kamax-io/mxisd/blob/master/docs/backends/ldap.md | ||||
|  | ||||
| ############### | ||||
| # SQL Backend # | ||||
| ############### | ||||
| # If you would like to integrate with a MySQL/MariaDB/PostgreQL/SQLite DB, | ||||
| # see https://github.com/kamax-io/mxisd/blob/master/docs/backends/sql.md | ||||
|  | ||||
| ################ | ||||
| # REST Backend # | ||||
| ################ | ||||
| # If you would like to integrate with an existing web service/webapp, | ||||
| # see https://github.com/kamax-io/mxisd/blob/master/docs/backends/rest.md | ||||
|  | ||||
|  | ||||
| ################################################# | ||||
| # Notifications for invites/addition to profile # | ||||
| ################################################# | ||||
| # If you would like to change the content, | ||||
| # see https://github.com/kamax-io/mxisd/blob/master/docs/threepids/notifications/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" | ||||
							
								
								
									
										184
									
								
								build.gradle
									
									
									
									
									
								
							
							
						
						
									
										184
									
								
								build.gradle
									
									
									
									
									
								
							| @@ -1,8 +1,8 @@ | ||||
| /* | ||||
|  * mxisd - Matrix Identity Server Daemon | ||||
|  * Copyright (C) 2017 Maxime Dor | ||||
|  * ma1sd - Matrix Identity Server Daemon | ||||
|  * Copyright (C) 2017 Kamax Sarl | ||||
|  * | ||||
|  * https://max.kamax.io/ | ||||
|  * 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 | ||||
| @@ -21,17 +21,21 @@ | ||||
| import java.util.regex.Pattern | ||||
|  | ||||
| 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 debBinPath = "/usr/lib/mxisd" | ||||
| def debConfPath = "/etc/mxisd" | ||||
| def debDataPath = "/var/lib/mxisd" | ||||
| def debBinPath = "/usr/lib/ma1sd" | ||||
| def debConfPath = "/etc/ma1sd" | ||||
| def debDataPath = "/var/lib/ma1sd" | ||||
| def debSystemdPath = "/etc/systemd/system" | ||||
|  | ||||
| def debConfFileName = "mxisd-sample.yaml" | ||||
| def debConfFileName = confFileName | ||||
| def debStartScriptFilename = "ma1sd" | ||||
|  | ||||
| def debBuildBasePath = "${project.buildDir}/tmp/debian" | ||||
| def debBuildDebianPath = "${debBuildBasePath}/DEBIAN" | ||||
| @@ -40,136 +44,156 @@ def debBuildConfPath = "${debBuildBasePath}${debConfPath}" | ||||
| def debBuildDataPath = "${debBuildBasePath}${debDataPath}" | ||||
| def debBuildSystemdPath = "${debBuildBasePath}${debSystemdPath}" | ||||
|  | ||||
| def dockerImageName = "kamax/mxisd" | ||||
| def dockerImageTag = "${dockerImageName}:${gitVersion()}" | ||||
| def dockerImageName = "ma1uta/ma1sd" | ||||
| def dockerImageTag = "${dockerImageName}:${ma1sdVersion()}" | ||||
|  | ||||
| group = 'io.kamax' | ||||
| mainClassName = 'io.kamax.mxisd.MxisdStandaloneExec' | ||||
| sourceCompatibility = '1.8' | ||||
| targetCompatibility = '1.8' | ||||
|  | ||||
| String ma1sdVersion() { | ||||
|     def versionPattern = Pattern.compile("v(\\d+\\.)?(\\d+\\.)?(\\d+)(-.*)?") | ||||
|  | ||||
|     String version = System.getenv('MA1SD_BUILD_VERSION') | ||||
|     if (version == null || version.size() == 0) { | ||||
|         version = gitVersion() | ||||
|     } | ||||
|     return versionPattern.matcher(version).matches() ? version.substring(1) : version | ||||
| } | ||||
|  | ||||
| String gitVersion() { | ||||
|     def versionPattern = Pattern.compile("v(\\d+\\.)?(\\d+\\.)?(\\d+)(-.*)?") | ||||
|     ByteArrayOutputStream out = new ByteArrayOutputStream() | ||||
|     exec { | ||||
|         commandLine = ['git', 'describe', '--tags', '--always', '--dirty'] | ||||
|         standardOutput = out | ||||
|     } | ||||
|     def v = out.toString().replace(System.lineSeparator(), '') | ||||
|     return versionPattern.matcher(v).matches() ? v.substring(1) : v | ||||
|     return out.toString().replace(System.lineSeparator(), '') | ||||
| } | ||||
|  | ||||
| buildscript { | ||||
|     repositories { | ||||
|         mavenCentral() | ||||
|         jcenter() | ||||
|     } | ||||
|  | ||||
|     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 { | ||||
|     maven { url "https://kamax.io/maven/releases/" } | ||||
|     maven { url "https://kamax.io/maven/snapshots/" } | ||||
|     mavenCentral() | ||||
|     jcenter() | ||||
| } | ||||
|  | ||||
| dependencies { | ||||
|     // Logging | ||||
|     compile 'org.slf4j:slf4j-simple:1.7.25' | ||||
|  | ||||
|     // Easy file management | ||||
|     compile 'commons-io:commons-io:2.5' | ||||
|     compile 'commons-io:commons-io:2.6' | ||||
|  | ||||
|     // Spring Boot - standalone app | ||||
|     compile 'org.springframework.boot:spring-boot-starter-web:1.5.10.RELEASE' | ||||
|     // Config management | ||||
|     compile 'org.yaml:snakeyaml:1.24' | ||||
|  | ||||
|     // Thymeleaf for HTML templates | ||||
|     compile "org.springframework.boot:spring-boot-starter-thymeleaf:1.5.10.RELEASE" | ||||
|     // Dependencies from old Matrix-java-sdk | ||||
|     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 | ||||
|     compile 'io.kamax:matrix-java-sdk:0.0.11' | ||||
|     // ORMLite | ||||
|     compile 'com.j256.ormlite:ormlite-jdbc:5.1' | ||||
|  | ||||
|     // ed25519 handling | ||||
|     compile 'net.i2p.crypto:eddsa:0.1.0' | ||||
|     compile 'net.i2p.crypto:eddsa:0.3.0' | ||||
|  | ||||
|     // LDAP connector | ||||
|     compile 'org.apache.directory.api:api-all:1.0.0' | ||||
|  | ||||
|     // DNS lookups | ||||
|     compile 'dnsjava:dnsjava:2.1.8' | ||||
|     compile 'dnsjava:dnsjava:2.1.9' | ||||
|  | ||||
|     // HTTP connections | ||||
|     compile 'org.apache.httpcomponents:httpclient:4.5.3' | ||||
|     compile 'org.apache.httpcomponents:httpclient:4.5.9' | ||||
|  | ||||
|     // Phone numbers validation | ||||
|     compile 'com.googlecode.libphonenumber:libphonenumber:8.7.1' | ||||
|     compile 'com.googlecode.libphonenumber:libphonenumber:8.10.15' | ||||
|  | ||||
|     // E-mail sending | ||||
|     compile 'com.sun.mail:javax.mail:1.5.6' | ||||
|     compile 'javax.mail:javax.mail-api:1.5.6' | ||||
|     compile 'javax.mail:javax.mail-api:1.6.2' | ||||
|     compile 'com.sun.mail:javax.mail:1.6.2' | ||||
|  | ||||
|     // Google Firebase Authentication backend | ||||
|     compile 'com.google.firebase:firebase-admin:5.3.0' | ||||
|  | ||||
|     // ORMLite | ||||
|     compile 'com.j256.ormlite:ormlite-jdbc:5.0' | ||||
|  | ||||
|     // Connection Pool | ||||
|     compile 'com.mchange:c3p0:0.9.5.2' | ||||
|     compile 'com.mchange:c3p0:0.9.5.4' | ||||
|  | ||||
|     // SQLite | ||||
|     compile 'org.xerial:sqlite-jdbc:3.20.0' | ||||
|     compile 'org.xerial:sqlite-jdbc:3.28.0' | ||||
|  | ||||
|     // PostgreSQL | ||||
|     compile 'org.postgresql:postgresql:42.1.4' | ||||
|     compile 'org.postgresql:postgresql:42.2.6' | ||||
|  | ||||
|     // 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 | ||||
|     compile 'com.twilio.sdk:twilio:7.14.5' | ||||
|     compile 'com.twilio.sdk:twilio:7.40.1' | ||||
|  | ||||
|     // SendGrid SDK to send emails from GCE | ||||
|     compile 'com.sendgrid:sendgrid-java:2.2.2' | ||||
|  | ||||
|     testCompile 'junit:junit:4.12' | ||||
|     testCompile 'com.github.tomakehurst:wiremock:2.8.0' | ||||
|     // ZT-Exec for exec identity store | ||||
|     compile 'org.zeroturnaround:zt-exec:1.11' | ||||
|  | ||||
|     // HTTP server | ||||
|     compile 'io.undertow:undertow-core:2.0.22.Final' | ||||
|  | ||||
|     // 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 { | ||||
|     executable = true | ||||
|  | ||||
|     embeddedLaunchScriptProperties = [ | ||||
|             confFolder: "/etc/default" | ||||
|     ] | ||||
| jar { | ||||
|     manifest { | ||||
|         attributes( | ||||
|                 'Implementation-Version': ma1sdVersion() | ||||
|         ) | ||||
|     } | ||||
| } | ||||
|  | ||||
| processResources { | ||||
| shadowJar { | ||||
|     baseName = project.name | ||||
|     classifier = null | ||||
|     version = null | ||||
| } | ||||
|  | ||||
| task debBuild(dependsOn: shadowJar) { | ||||
|     doLast { | ||||
|         copy { | ||||
|             from('build/resources/main/application.yaml') { | ||||
|                 rename 'application.yaml', 'mxisd.yaml' | ||||
|             } | ||||
|             into 'build/resources/main' | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| task buildDeb(dependsOn: build) { | ||||
|     doLast { | ||||
|         def v = gitVersion() | ||||
|         println "Version for package: ${v}" | ||||
|         String debVersion = ma1sdVersion() | ||||
|         println "Version for package: ${debVersion}" | ||||
|         mkdir distDir | ||||
|         mkdir debBuildBasePath | ||||
|         mkdir "${debBuildBasePath}/DEBIAN" | ||||
|         mkdir debBuildDebianPath | ||||
|         mkdir debBuildBinPath | ||||
|         mkdir debBuildConfPath | ||||
|         mkdir debBuildDataPath | ||||
|         mkdir debBuildSystemdPath | ||||
|  | ||||
|         copy { | ||||
|             from "${project.buildDir}/libs/mxisd.jar" | ||||
|             from "${project.buildDir}/libs/ma1sd.jar" | ||||
|             into debBuildBinPath | ||||
|         } | ||||
|  | ||||
|         ant.chmod( | ||||
|                 file: "${debBuildBinPath}/mxisd.jar", | ||||
|                 perm: 'a+x' | ||||
|         ) | ||||
|         copy { | ||||
|             from "${project.file("src/script/" + debStartScriptFilename)}" | ||||
|             into debBuildBinPath | ||||
|         } | ||||
|  | ||||
|         copy { | ||||
|             from(project.file(confFileName)) { | ||||
| @@ -178,16 +202,16 @@ task buildDeb(dependsOn: build) { | ||||
|             into debBuildConfPath | ||||
|         } | ||||
|  | ||||
|         ant.replaceregexp( | ||||
|         ant.replaceregexp( // FIXME adapt to new config format | ||||
|                 file: "${debBuildConfPath}/${debConfFileName}", | ||||
|                 match: "key.path:(.*)", | ||||
|                 replace: "key.path: '${debDataPath}/signing.key'" | ||||
|                 match: "key:\\R  path:(.*)", | ||||
|                 replace: "key:\n  path: '${debDataPath}/keys'" | ||||
|         ) | ||||
|  | ||||
|         ant.replaceregexp( | ||||
|         ant.replaceregexp( // FIXME adapt to new config format | ||||
|                 file: "${debBuildConfPath}/${debConfFileName}", | ||||
|                 match: "storage.provider.sqlite.database:(.*)", | ||||
|                 replace: "storage.provider.sqlite.database: '${debDataPath}/mxisd.db'" | ||||
|                 match: "storage:\\R  provider:\\R    sqlite:\\R      database:(.*)", | ||||
|                 replace: "storage:\n  provider:\n    sqlite:\n      database: '${debDataPath}/store.db'" | ||||
|         ) | ||||
|  | ||||
|         copy { | ||||
| @@ -198,7 +222,7 @@ task buildDeb(dependsOn: build) { | ||||
|         ant.replace( | ||||
|                 file: "${debBuildDebianPath}/control", | ||||
|                 token: 'Version: 0', | ||||
|                 value: "Version: ${v}" | ||||
|                 value: "Version: ${debVersion}" | ||||
|         ) | ||||
|  | ||||
|         ant.replace( | ||||
| @@ -207,6 +231,12 @@ task buildDeb(dependsOn: build) { | ||||
|                 value: debDataPath | ||||
|         ) | ||||
|  | ||||
|         ant.replace( | ||||
|                 file: "${debBuildDebianPath}/postinst", | ||||
|                 token: '%DEB_CONF_FILE%', | ||||
|                 value: "${debConfPath}/ma1sd.yaml" | ||||
|         ) | ||||
|  | ||||
|         ant.chmod( | ||||
|                 file: "${debBuildDebianPath}/postinst", | ||||
|                 perm: 'a+x' | ||||
| @@ -218,7 +248,7 @@ task buildDeb(dependsOn: build) { | ||||
|         ) | ||||
|  | ||||
|         copy { | ||||
|             from "${project.file('src/systemd/mxisd.service')}" | ||||
|             from "${project.file('src/systemd/ma1sd.service')}" | ||||
|             into debBuildSystemdPath | ||||
|         } | ||||
|  | ||||
| @@ -234,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 | ||||
|  | ||||
|     doLast { | ||||
|   | ||||
| @@ -1,8 +1,11 @@ | ||||
| # Table of Contents | ||||
| - [Identity Concepts in Matrix](concepts.md) | ||||
| - [Getting Started](getting-started.md) | ||||
| - [Build from sources](build.md) (Optional) | ||||
| - Installation | ||||
|   - [Debian package](install/debian.md) | ||||
|   - [ArchLinux](install/archlinux.md) | ||||
|   - [NixOS](install/nixos.md) | ||||
|   - [Docker](install/docker.md) | ||||
|   - [From source](install/source.md) | ||||
| - [Architecture overview](architecture.md) | ||||
| @@ -20,3 +23,4 @@ | ||||
|     - [SendGrid](threepids/notification/sendgrid-handler.md) | ||||
| - [Sessions](threepids/session/session.md) | ||||
|   - [Views](threepids/session/session-views.md) | ||||
| - [FAQ](faq.md) | ||||
|   | ||||
| @@ -1 +1 @@ | ||||
| theme: jekyll-theme-cayman | ||||
| theme: jekyll-theme-hacker | ||||
| @@ -16,19 +16,16 @@ TCP 443 | ||||
|           +<---------------------------------<+ | ||||
|           | | ||||
|           |   +-------------------+ | ||||
|  TCP 8090 +-> | mxisd             | | ||||
|  TCP 8090 +-> | ma1sd             | | ||||
|               |                   | | ||||
|               | - Profile's 3PIDs >----+ | ||||
|               | - 3PID Invites    |    |             +--------------------------+ | ||||
|               +-|-----------------+    +>----------> | Central Identity service | | ||||
|                 |                      |   TCP 443   | Matrix.org / Vector.im   | | ||||
|                 |                      |             +--------------------------+ | ||||
|                 +>-------------------->+ | ||||
|               | - Profile's 3PIDs | | ||||
|               | - 3PID Invites    | | ||||
|               +-|-----------------+ | ||||
|                 | | ||||
|              TCP 443 | ||||
|                 |  +------------------------+ | ||||
|                 |  | Remote Federated       | | ||||
|                 |  | mxisd servers          | | ||||
|                 |  | ma1sd servers          | | ||||
|                 |  |                        | | ||||
|                 +--> - 3PID Invites         | | ||||
|                    +------------------------+ | ||||
|   | ||||
| @@ -12,30 +12,31 @@ | ||||
|  | ||||
| ### Build | ||||
| ```bash | ||||
| git clone https://github.com/kamax-io/mxisd.git | ||||
| cd mxisd | ||||
| git clone https://github.com/ma1uta/ma1sd.git | ||||
| cd ma1sd | ||||
| ./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). | ||||
| **NOTE**: `application.yaml` is also called `mxisd.yaml` in some specific installations. | ||||
|  | ||||
| Start the server in foreground to validate the build and configuration: | ||||
| ```bash | ||||
| java -jar build/libs/mxisd.jar | ||||
| java -jar build/libs/ma1sd.jar | ||||
| ``` | ||||
|  | ||||
| Ensure the signing key is available: | ||||
| ```bash | ||||
| $ curl 'http://localhost:8090/_matrix/identity/api/v1/pubkey/ed25519:0' | ||||
|  | ||||
| {"public_key":"..."} | ||||
| ``` | ||||
|  | ||||
| Test basic recursive lookup (requires Internet connection with access to TCP 443): | ||||
| ```bash | ||||
| $ curl 'http://localhost:8090/_matrix/identity/api/v1/lookup?medium=email&address=mxisd-federation-test@kamax.io' | ||||
| {"address":"mxisd-federation-test@kamax.io","medium":"email","mxid":"@mxisd-lookup-test:kamax.io",...} | ||||
| $ curl 'http://localhost:8090/_matrix/identity/api/v1/lookup?medium=email&address=ma1sd-federation-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 | ||||
| @@ -55,15 +56,15 @@ Requirements: | ||||
| - fakeroot | ||||
| - dpkg-deb | ||||
|  | ||||
| [Build mxisd](#build) then: | ||||
| [Build ma1sd](#build) then: | ||||
| ```bash | ||||
| ./gradlew buildDeb  | ||||
| ./gradlew debBuild | ||||
| ``` | ||||
| You will find the debian package in `build/dist`.   | ||||
| Then follow the instruction in the [Debian package](install/debian.md) document. | ||||
|  | ||||
| ## Docker image | ||||
| [Build mxisd](#build) then: | ||||
| [Build ma1sd](#build) then: | ||||
| ```bash | ||||
| ./gradlew dockerBuild | ||||
| ``` | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| # Concepts | ||||
| - [Matrix](#matrix) | ||||
| - [mxisd](#mxisd) | ||||
| - [ma1sd](#ma1sd) | ||||
|  | ||||
| ## Matrix | ||||
| 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.) | ||||
| - 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 | ||||
| Where a user **account and data** are stored. | ||||
| @@ -34,10 +34,10 @@ An Identity server: | ||||
| ### 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. | ||||
|  | ||||
| ## mxisd | ||||
| The following concepts are specific to mxisd. | ||||
| ## ma1sd | ||||
| The following concepts are specific to ma1sd. | ||||
|  | ||||
| ### Identity store | ||||
| 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. | ||||
|   | ||||
| @@ -1,7 +1,6 @@ | ||||
| # Configuration | ||||
| - [Concepts](#concepts) | ||||
|   - [Syntax](#syntax) | ||||
|   - [Variables](#variables) | ||||
| - [Matrix](#matrix) | ||||
| - [Server](#server) | ||||
| - [Storage](#storage) | ||||
| @@ -11,36 +10,15 @@ | ||||
|  | ||||
| ## Concepts | ||||
| ### Syntax | ||||
| The configuration file is [YAML](http://yaml.org/) based, allowing two types of syntax. | ||||
|  | ||||
| Properties-like: | ||||
| ```yaml | ||||
| my.config.item: 'value' | ||||
| ``` | ||||
|  | ||||
| Object-like: | ||||
| The configuration file is [YAML](http://yaml.org/) based: | ||||
| ```yaml | ||||
| my: | ||||
|   config: | ||||
|     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.domain` | ||||
| @@ -53,17 +31,19 @@ Namespace to create arbitrary list of Identity servers, usable in other parts of | ||||
|  | ||||
| Example: | ||||
| ```yaml | ||||
| matrix.identity.servers: | ||||
| matrix: | ||||
|   identity: | ||||
|     servers: | ||||
|       myOtherServers: | ||||
|         - 'https://other1.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.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.publicUrl`: Defaults to `https://${server.name}` | ||||
| - `server.publicUrl`: Defaults to `https://{server.name}` | ||||
|  | ||||
| ## Storage | ||||
| ### SQLite | ||||
| @@ -82,19 +62,23 @@ See the dedicated documents: | ||||
|  | ||||
| Example: | ||||
| ```yaml | ||||
| notification.handler.email: 'sendgrid' | ||||
| notification.handler.msisdn: 'raw' | ||||
| notification: | ||||
|   handler: | ||||
|     email: 'sendgrid' | ||||
|     msisdn: 'raw' | ||||
| ``` | ||||
| - 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 | ||||
| - `notification.handers.<handler ID>`: Handler-specific configuration for the given handler ID. Repeatable. | ||||
|  | ||||
| Example: | ||||
| ```yaml | ||||
| notification.handlers.raw: ... | ||||
| notification.handlers.sendgrid: ... | ||||
| notification: | ||||
|   handlers: | ||||
|     raw: ... | ||||
|     sendgrid: ... | ||||
| ``` | ||||
|  | ||||
| Built-in: | ||||
|   | ||||
							
								
								
									
										68
									
								
								docs/faq.md
									
									
									
									
									
								
							
							
						
						
									
										68
									
								
								docs/faq.md
									
									
									
									
									
								
							| @@ -10,22 +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. | ||||
| - [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. | ||||
|  | ||||
| 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. | ||||
|  | ||||
| ### 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. | ||||
|  | ||||
| In its default configuration, mxisd will talk to the central Matrix Identity servers and use other federated public | ||||
| servers when performing queries, giving you access to at least the same information as if you were not running it. | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| So ma1sd 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. | ||||
|  | ||||
| ### I'm not sure I understand what an "Identity server" is supposed to be or do... | ||||
| @@ -34,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". | ||||
|  | ||||
| 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. | ||||
|  | ||||
| ### 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. | ||||
|  | ||||
| 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? | ||||
| The [synapse LDAP3 auth provider](https://github.com/matrix-org/matrix-synapse-ldap3) is not longer maintained and | ||||
| only handles on specific flow: validate credentials at login. | ||||
| ### I already use the synapse LDAP3 auth provider. Why should I care about ma1sd? | ||||
| The [synapse LDAP3 auth provider](https://github.com/matrix-org/matrix-synapse-ldap3) is not longer maintained despite | ||||
| saying so and only handles on specific flow: validate credentials at login. | ||||
|  | ||||
| It does not: | ||||
| - Auto-provision user profiles | ||||
| - Integrate with Identity management | ||||
| - 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. | ||||
|  | ||||
| ### Sydent is the official Identity server implementation of the Matrix team. Why not use that? | ||||
| @@ -65,25 +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 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. | ||||
|  | ||||
| In its default configuration, mxisd act as a proxy to Matrix.org/Vector.im. You will have access to the same data and | ||||
| behaviour than if you were using them directly. There is no downside in using mxisd with the default configuration. | ||||
| 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. | ||||
|  | ||||
| mxisd can also be configured not 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! | ||||
| mxisd primary concern is 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. | ||||
| ### So ma1sd is just a big hack! I don't want to use non-official features! | ||||
| 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, ma1sd will follow, remaining 100% compatible with the ecosystem. | ||||
|  | ||||
| Therefore, using mxisd is a safe choice. It will be like using the central Matrix.org Identity servers, yet not closing | ||||
| the door to a growing list of enhancements and integrations. | ||||
|  | ||||
| ### 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. | ||||
|  | ||||
| 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. | ||||
|   | ||||
| @@ -3,7 +3,7 @@ | ||||
| - [Basic](#basic) | ||||
|   - [Overview](#overview) | ||||
|   - [synapse](#synapse) | ||||
|   - [mxisd](#mxisd) | ||||
|   - [ma1sd](#ma1sd) | ||||
|   - [Validate](#validate) | ||||
|   - [Next steps](#next-steps) | ||||
|     - [Profile auto-fil](#profile-auto-fill) | ||||
| @@ -16,15 +16,15 @@ | ||||
|     - [DNS Overwrite](#dns-overwrite) | ||||
|  | ||||
| ## Description | ||||
| Authentication is an enhanced feature of mxisd to ensure coherent and centralized identity management.   | ||||
| It allows to use Identity stores configured in mxisd to authenticate users on your Homeserver. | ||||
| Authentication is an enhanced feature of ma1sd to ensure coherent and centralized identity management.   | ||||
| It allows to use Identity stores configured in ma1sd to authenticate users on your Homeserver. | ||||
|  | ||||
| Authentication is divided into two parts: | ||||
| - [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 | ||||
| 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. | ||||
|  | ||||
| ### Overview | ||||
| @@ -33,7 +33,7 @@ An overview of the Basic Authentication process: | ||||
|                                                                                     Identity stores | ||||
|  Client                                                                             +------+ | ||||
|    |                                            +-------------------------+    +--> | LDAP | | ||||
|    |   +---------------+  /_matrix/identity     | mxisd                   |    |    +------+ | ||||
|    |   +---------------+  /_matrix/identity     | ma1sd                   |    |    +------+ | ||||
|    +-> | Reverse proxy | >------------------+   |                         |    | | ||||
|        +--|------------+                    |   |                         |    |    +--------+ | ||||
|           |                                 +-----> 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) | ||||
|               +--------------------------+ | ||||
| ``` | ||||
| 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 | ||||
| - 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: | ||||
|   - As described by the auth module documentation | ||||
|   - Set `endpoint` to `http://mxisdAddress:8090` - Replace `mxisdAddress` by an IP/host name that provides a direct | ||||
|   connection to mxisd.   | ||||
|   - Set `endpoint` to `http://ma1sdAddress:8090` - Replace `ma1sdAddress` by an IP/host name that provides a direct | ||||
|   connection to ma1sd.   | ||||
|   This **MUST NOT** be a public address, and SHOULD NOT go through a reverse proxy. | ||||
| - Restart synapse | ||||
|  | ||||
| ### mxisd | ||||
| ### ma1sd | ||||
| - Configure and enable at least one [Identity store](../stores/README.md) | ||||
| - Restart mxisd | ||||
| - Restart ma1sd | ||||
|  | ||||
| ### Validate | ||||
| Login on the Homeserver using credentials present in one of your Identity stores. | ||||
| @@ -74,7 +74,15 @@ See your Identity store [documentation](../stores/README.md) on how to enable th | ||||
|  | ||||
|  | ||||
| ## Advanced | ||||
| The Authentication feature allows users to login to their Homeserver by using their 3PIDs in a configured Identity store. | ||||
| The Authentication feature allows users to: | ||||
| - Rewrite usernames matching a pattern to be mapped to another username via a 3PID. | ||||
| - login to their Homeserver by using their 3PIDs in a configured Identity store. | ||||
|  | ||||
| This feature also allows to work around the following issues: | ||||
| - Lowercase all usernames for synapse, allowing case-insensitive login | ||||
| - Unable to login on synapse if username is numerical | ||||
| - Any generic transformation of username prior to sending to synapse, bypassing the restriction that password providers | ||||
| cannot change the localpart being authenticated. | ||||
|  | ||||
| ### Overview | ||||
| This is performed by intercepting the Homeserver endpoint `/_matrix/client/r0/login` as depicted below: | ||||
| @@ -85,7 +93,7 @@ This is performed by intercepting the Homeserver endpoint `/_matrix/client/r0/lo | ||||
|             |                            |     Step 1    +---------------------------+     Step 2 | ||||
|             |                            |               |                           | | ||||
| Client+---->| /_matrix/client/r0/login +---------------->|                           | Look up address  +---------+ | ||||
|             |                      ^     |               |  mxisd - Identity server  +----------------->| Backend | | ||||
|             |                      ^     |               |  ma1sd - Identity server  +----------------->| Backend | | ||||
|             |                      |     |               |                           |                  +---------+ | ||||
|             | /_matrix/* +--+      +---------------------+                           | | ||||
|             |               |            |               +---------------+-----------+ | ||||
| @@ -102,17 +110,17 @@ Client+---->| /_matrix/client/r0/login +---------------->| | ||||
| ``` | ||||
|  | ||||
| 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. | ||||
| 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. | ||||
| 4. The response from the Homeserver is sent back to the client, believing it was the HS which directly answered. | ||||
|  | ||||
| ### Requirements | ||||
| - [Basic Authentication configured and working](#basic) | ||||
| - Reverse proxy setup | ||||
| - Homeserver | ||||
| - Compatible [Identity store](../stores/README.md) | ||||
| - [Basic Authentication configured and working](#basic) | ||||
| - Client and Homeserver using the [C2S API r0.4.x](https://matrix.org/docs/spec/client_server/r0.4.0.html) or later | ||||
| - Reverse proxy setup | ||||
|  | ||||
| ### Configuration | ||||
| #### Reverse Proxy | ||||
| @@ -121,7 +129,7 @@ The specific configuration to put under the relevant `VirtualHost`: | ||||
| ```apache | ||||
| 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: | ||||
| ```apache | ||||
| @@ -137,18 +145,107 @@ Your VirtualHost should now look similar to: | ||||
| </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 | ||||
| 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. | ||||
|  | ||||
| To do so, put the following configuration in your mxisd configuration: | ||||
| To do so, put the following configuration in your ma1sd configuration: | ||||
| ```yaml | ||||
| dns.overwrite.homeserver.client: | ||||
| dns: | ||||
|   overwrite: | ||||
|     homeserver: | ||||
|       client: | ||||
|         - name: 'example.org' | ||||
|           value: 'http://localhost:8008' | ||||
| ``` | ||||
| `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 the `matrix.domain` configuration option and avoid duplicating it. | ||||
| You can use `${server.name}` to auto-populate the `value` using the `server.name` configuration option and avoid duplicating it. | ||||
| In case the hostname is the same as your Matrix domain and `server.name` is not explicitely set in the config, `server.name` will default to | ||||
| `matrix.domain` and will still probably have the correct value. | ||||
|  | ||||
| `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 | ||||
| In ma1sd config: | ||||
| ```yaml | ||||
| auth: | ||||
|   rewrite: | ||||
|     user: | ||||
|       rules: | ||||
|         - regex: <your regexp> | ||||
|           medium: 'your.custom.medium.type' | ||||
| ``` | ||||
| `rules` takes a list of rules. Rules have two properties: | ||||
| - `regexp`: The regex pattern to match. This **MUST** match the full string. See [Java regex](https://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html) for syntax. | ||||
| - `medium`: Custom 3PID type that will be used in the 3PID lookup. This can be anything you want and needs to be supported | ||||
| by your Identity store config and/or code. | ||||
|  | ||||
| Rules are matched in listed order. | ||||
|  | ||||
| Common regexp patterns: | ||||
| - Numerical usernames: `[0-9]+` | ||||
|  | ||||
| ##### LDAP Example | ||||
| If your users use their numerical employee IDs, which cannot be used with synapse, you can make it work with (relevant config only): | ||||
| ```yaml | ||||
| auth: | ||||
|   rewrite: | ||||
|     user: | ||||
|       rules: | ||||
|         - regex: '[0-9]+' | ||||
|           medium: 'kmx.employee.id' | ||||
|            | ||||
| ldap: | ||||
|   attribute: | ||||
|     threepid: | ||||
|       kmx.employee.id: | ||||
|         - 'ldapAttributeForEmployeeId' | ||||
| ``` | ||||
|   | ||||
| @@ -1,19 +1,22 @@ | ||||
| # 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. | ||||
|  | ||||
| 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.   | ||||
| 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. | ||||
|  | ||||
| ## Configuration | ||||
| ```yaml | ||||
| lookup.recursive.bridge.enabled: <boolean> | ||||
| lookup.recursive.bridge.recursiveOnly: <boolean> | ||||
| lookup.recursive.bridge.server: <URL to the bridge endpoint for all 3PID medium> | ||||
| lookup.recursive.bridge.mappings: | ||||
| lookup: | ||||
|   recursive: | ||||
|     bridge: | ||||
|       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> | ||||
|  | ||||
| ``` | ||||
|   | ||||
| @@ -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 | ||||
| internally, so users can just find themselves **prior** to having any common room(s) | ||||
| - 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 | ||||
| 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 | ||||
|            |                                              Step 1    +-------------------------+ | ||||
|            |   /_matrix/client/r0/user_directory/search ----------> |                         |  Search in   +---------+ | ||||
|            |                        /\                              | mxisd - Identity server | -----------> | Backend | | ||||
|            |                        /\                              | ma1sd - Identity server | -----------> | Backend | | ||||
|            |   /_matrix/*            \----------------------------- |                         |  all users   +---------+ | ||||
|            |        |            Step 4: Send back merged results   +-------------------------+ | ||||
|            +        |                                                            | | ||||
| @@ -44,7 +44,7 @@ Client --> | Reverse proxy | ||||
|                          +------------+   /_matrix/client/r0/user_directory/search | ||||
| ``` | ||||
| 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. | ||||
| 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. | ||||
| @@ -52,7 +52,7 @@ Steps: | ||||
| which directly answered the request. | ||||
|  | ||||
| ## 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 | ||||
|    | ||||
| ## Configuration | ||||
| @@ -62,7 +62,7 @@ The specific configuration to put under the relevant `VirtualHost`: | ||||
| ```apache | ||||
| 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. | ||||
|  | ||||
| Your `VirtualHost` should now look like this: | ||||
| @@ -118,25 +118,36 @@ server { | ||||
| ``` | ||||
|  | ||||
| ### 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. | ||||
|  | ||||
| To do so, use the following configuration: | ||||
| ```yaml | ||||
| dns.overwrite.homeserver.client: | ||||
| dns: | ||||
|   overwrite: | ||||
|     homeserver: | ||||
|       client: | ||||
|         - name: 'example.org' | ||||
|           value: 'http://localhost:8008' | ||||
| ``` | ||||
| `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 | ||||
| the `matrix.domain` configuration option and avoid duplicating it. | ||||
|  | ||||
| `value` is the base internal URL of the Homeserver, without any `/_matrix/..` or trailing `/`. | ||||
| - `name` must be the hostname of the URL that clients use when connecting to the Homeserver. | ||||
| - `value` is the base internal URL of the Homeserver, without any `/_matrix/..` or trailing `/`. | ||||
|  | ||||
| ## Next steps | ||||
| ### Homeserver results | ||||
| 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 | ||||
| directory.exclude.homeserever: true | ||||
| directory: | ||||
|   exclude: | ||||
|     homeserver: true | ||||
| ``` | ||||
|  | ||||
| ### 3PID exclusion in 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: | ||||
| ```yaml | ||||
| directory: | ||||
|   exclude: | ||||
|     threepid: true | ||||
| ``` | ||||
|   | ||||
							
								
								
									
										122
									
								
								docs/features/experimental/application-service.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								docs/features/experimental/application-service.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,122 @@ | ||||
| # Application Service | ||||
| **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](https://matrix.org/docs/spec/application_service/r0.1.1.html). | ||||
|  | ||||
| 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 | ||||
| account was already provisioned on the Homeserver. | ||||
|  | ||||
| #### Requirements | ||||
| - [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. | ||||
|  | ||||
| #### Configuration | ||||
| In your ma1sd config file: | ||||
| ```yaml | ||||
| synapseSql: | ||||
|   enabled: false ## Do not use this line if Synapse is used as an Identity Store | ||||
|   type: '<DB TYPE>' | ||||
|   connection: '<DB CONNECTION URL>' | ||||
| ``` | ||||
|  | ||||
| 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). | ||||
| 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.   | ||||
| See [the Template generator documentation](../../threepids/notification/template-generator.md) for more info. | ||||
|  | ||||
| #### Test | ||||
| Invite a user which is part of your domain while an appropriate Identity store is used. | ||||
|  | ||||
| ### Auto-reject of expired 3PID invites | ||||
| *TBC* | ||||
							
								
								
									
										16
									
								
								docs/features/experimental/profile.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								docs/features/experimental/profile.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| # Profile | ||||
| **WARNING**: The following sub-features are considered experimental and not officially supported. Use at your own peril. | ||||
|  | ||||
| ## Public Profile enhancement | ||||
| This feature allows to enhance a public profile query with more info than just Matrix ID and Display name, allowing for | ||||
| custom applications to retrieve custom data not currently provided by synapse, per example. | ||||
|  | ||||
| **WARNING**: This information can be queried without authentication as per the specification. Do not enable unless in a | ||||
| controlled environment. | ||||
|  | ||||
| ### Configuration | ||||
| #### Reverse proxy | ||||
| ##### Apache | ||||
| ```apache | ||||
| ProxyPassMatch "^/_matrix/client/r0/profile/([^/]+)$" "http://127.0.0.1:8090/_matrix/client/r0/profile/$1" | ||||
| ``` | ||||
| @@ -5,28 +5,26 @@ 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.   | ||||
| On the other hand, Phone numbers cannot be resolved this way. | ||||
|  | ||||
| For 3PIDs which are not compatible with the DNS system, mxisd will talk to the central Identity server of matrix.org by | ||||
| default. | ||||
| 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. | ||||
|  | ||||
| Outbound federation is enabled by default while inbound federation is opt-in and require a specific DNS record. | ||||
|  | ||||
| ## Overview | ||||
| ``` | ||||
|               +-------------------+   +-------------> +----------+ | ||||
|               | mxisd             |   |               | Backends | | ||||
|               | ma1sd             |   |               | Backends | | ||||
|               |                   |   |      +------> +----------+ | ||||
|               |                   |   |      | | ||||
|               | Invites / Lookups |   |      | | ||||
|  Federated    | +--------+        |   |      |        +-------------------+ | ||||
|  Identity  ---->| Remote |>-----------+      +------> | Remote Federated  | | ||||
|  Server       | +--------+        |          |        | mxisd servers     | | ||||
|               |                   |          |        +-------------------+ | ||||
|               | +--------+        |          | | ||||
|  Homeserver --->| Local  |>------------------+ | ||||
|  and clients  | +--------+        |          |        +--------------------------+  | ||||
|               +-------------------+          +------> | Central Identity service | | ||||
|                                                       | Matrix.org / Vector.im   | | ||||
|                                                       +--------------------------+ | ||||
|  Federated    | +--------+        |   |      | | ||||
|  Identity  ---->| Remote |>-----------+      | | ||||
|  Server       | +--------+        |          | | ||||
|               |                   |          | | ||||
|               | +--------+        |          |        +-------------------+ | ||||
|  Homeserver --->| Local  |>------------------+------> | Remote Federated  | | ||||
|  and clients  | +--------+        |                   | ma1sd servers     | | ||||
|               +-------------------+                   +-------------------+ | ||||
| ``` | ||||
|  | ||||
| ## Inbound | ||||
| @@ -36,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. | ||||
| ```  | ||||
|  | ||||
| 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 | ||||
| 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 | ||||
| lookup.recursive.enabled: false | ||||
| invite.resolution.recursive: false | ||||
| session.policy.validation.forLocal.toRemote.enabled: false | ||||
| session.policy.validation.forRemote.toRemote.enabled: false | ||||
| lookup: | ||||
|   recursive: | ||||
|     enabled: false | ||||
| invite: | ||||
|   resolution: | ||||
|     recursive: false | ||||
| ```  | ||||
|  | ||||
| There is currently no way to selectively disable federation towards specific servers, but this feature is planned. | ||||
|   | ||||
| @@ -1,10 +1,96 @@ | ||||
| # 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) | ||||
|  | ||||
| ## Room Invitations | ||||
| Resolution can be customized using the following configuration: | ||||
| ## Lookups | ||||
| 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: | ||||
| ```yaml | ||||
| forward: | ||||
|   servers: | ||||
|     - 'matrix-org' | ||||
| ``` | ||||
| **NOTE:** You should carefully consider enabling this option, which is discouraged.   | ||||
| For more info, see the [relevant issue](https://github.com/kamax-matrix/ma1sd/issues/76). | ||||
|  | ||||
| ## Invitations | ||||
| ### 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`   | ||||
| - Default value: `true`   | ||||
| @@ -15,7 +101,7 @@ Resolution can be customized using the following configuration: | ||||
|  | ||||
| `invite.resolution.timer`   | ||||
| - 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) | ||||
|   | ||||
| @@ -1,9 +1,10 @@ | ||||
| # Profile enhancement | ||||
| **WARNING**: Alpha feature not officially supported. Do not use. | ||||
| # Profile | ||||
| The profile feature does not do anything on its own and acts as a support feature for others, allowing to retrieve | ||||
| information about a user based on its Matrix ID by querying enabled [Identity stores](../stores/README.md). | ||||
|  | ||||
| ## Configuration | ||||
| ### Reverse proxy | ||||
| #### Apache | ||||
| ```apache | ||||
| ProxyPassMatch "^/_matrix/client/r0/profile/([^/]+)$" "http://127.0.0.1:8090/_matrix/client/r0/profile/$1" | ||||
| ``` | ||||
| Currently supported: | ||||
| - Display name | ||||
| - 3PIDs | ||||
| - Roles/Groups | ||||
|  | ||||
| Experimental sub-features are also available. See [the dedicated document](experimental/profile.md). | ||||
|   | ||||
							
								
								
									
										111
									
								
								docs/features/registration.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								docs/features/registration.md
									
									
									
									
									
										Normal 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. | ||||
| @@ -6,57 +6,71 @@ | ||||
| 5. [Validate](#validate) | ||||
| 6. [Next steps](#next-steps) | ||||
|  | ||||
| Following these quick start instructions, you will have a basic setup that can perform recursive/federated lookups and | ||||
| talk to the central Matrix.org Identity server.   | ||||
| Following these quick start instructions, you will have a basic setup that can perform recursive/federated lookups.   | ||||
| 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 | ||||
| You will need: | ||||
| - Homeserver | ||||
| - Reverse proxy with regular TLS/SSL certificate (Let's encrypt) for your mxisd domain | ||||
| - Working Homeserver, ideally with working federation | ||||
| - 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 | ||||
| not support HTTPS listener at this time. | ||||
| If you use synapse: | ||||
| - 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-io/mxisd/wiki/Gotchas#nating) if you use the same | ||||
| hostname. | ||||
| Be aware of a [NAT/Reverse proxy gotcha](https://github.com/ma1uta/ma1sd/wiki/Gotchas#nating) if you use the same | ||||
| 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 | ||||
| [dedicated document](architecture.md) | ||||
|  | ||||
| ## Install | ||||
| Install via: | ||||
| - [Debian package](install/debian.md) | ||||
| - [Docker image](install/docker.md) | ||||
| - [Debian package](install/debian.md) | ||||
| - [ArchLinux](install/archlinux.md) | ||||
| - [NixOS](install/nixos.md) | ||||
| - [Sources](build.md) | ||||
|  | ||||
| See the [Latest release](https://github.com/kamax-io/mxisd/releases/latest) for links to each. | ||||
| See the [Latest release](https://github.com/ma1uta/ma1sd/releases/latest) for links to each. | ||||
|  | ||||
| ## 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): | ||||
| ```yaml | ||||
| matrix.domain: 'example.org' | ||||
| key.path: '/path/to/signing.key.file' | ||||
| storage.provider.sqlite.database: '/path/to/mxisd.db' | ||||
| ```   | ||||
| > **NOTE**: Details about configuration syntax and format are described [here](configure.md) | ||||
|  | ||||
| If you haven't created a configuration file yet, copy `ma1sd.example.yaml` to where the configuration file is stored given | ||||
| your installation method and edit to your needs. | ||||
|  | ||||
| The following items must be at least configured: | ||||
| - `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. | ||||
| - `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). | ||||
|  | ||||
| ## 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 | ||||
| #### Apache2 | ||||
| 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!** | ||||
| ```apache | ||||
| ProxyPass /_matrix/identity http://0.0.0.0:8090/_matrix/identity | ||||
| @@ -65,9 +79,9 @@ ProxyPass /_matrix/identity http://0.0.0.0:8090/_matrix/identity | ||||
| Typical configuration would look like: | ||||
| ```apache | ||||
| <VirtualHost *:443> | ||||
|     ServerName example.org | ||||
|     ServerName matrix.example.org | ||||
|      | ||||
|     ... | ||||
|     # ... | ||||
|      | ||||
|     ProxyPreserveHost on | ||||
|     ProxyPass /_matrix/identity http://localhost:8090/_matrix/identity | ||||
| @@ -77,7 +91,7 @@ Typical configuration would look like: | ||||
|  | ||||
| #### nginx | ||||
| 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!** | ||||
| ```nginx | ||||
| location /_matrix/identity { | ||||
| @@ -89,9 +103,9 @@ Typical configuration would look like: | ||||
| ```nginx | ||||
| server { | ||||
|     listen 443 ssl; | ||||
|     server_name example.org; | ||||
|     server_name matrix.example.org; | ||||
|      | ||||
|     ... | ||||
|     # ... | ||||
|      | ||||
|     location /_matrix/identity { | ||||
|         proxy_pass http://localhost:8090/_matrix/identity; | ||||
| @@ -108,28 +122,36 @@ server { | ||||
| ``` | ||||
|  | ||||
| ### 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: | ||||
| ```yaml | ||||
| 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 | ||||
| your own Identity server is authoritative for your HS. | ||||
| It is **highly recommended** to remove `matrix.org` and `vector.im` (or any other default entry) from your configuration | ||||
| so only your own Identity server is authoritative for your HS. | ||||
|  | ||||
| ## Validate | ||||
| Log in using your Matrix client and set `https://example.org` as your Identity server URL, replacing `example.org` by | ||||
| the relevant hostname which you configured in your reverse proxy.   | ||||
| Invite `mxisd-federation-test@kamax.io` to a room, which should be turned into a Matrix invite to `@mxisd-lookup-test:kamax.io`. | ||||
| At this point, the test user will join the room, send a congratulation message and leave.   | ||||
| ## Validate (Under reconstruction) | ||||
| **NOTE:** In case your homeserver has no working federation, step 5 will not happen. If step 4 took place, consider | ||||
| your installation validated. | ||||
|  | ||||
| 1. Log in using your Matrix client and set `https://matrix.example.org` as your Identity server URL, replacing `matrix.example.org` | ||||
| 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. | ||||
| 3. Invite `ma1sd-federation-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. | ||||
| **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 did not work, [get in touch](../README.md#support) and we'll do our best to get you started. | ||||
| If it worked, it means you are up and running and can enjoy ma1sd in its basic mode! Congratulations!   | ||||
| 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 | ||||
| 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: | ||||
|  | ||||
| - [Enable extra features](features/) | ||||
| - [Use your own Identity stores](stores/README.md) | ||||
| - [Hardening your ma1sd installation](install/security.md) | ||||
| - [Learn about day-to-day operations](operations.md) | ||||
|   | ||||
| @@ -1,3 +1,9 @@ | ||||
| --- | ||||
|  | ||||
| ** Outdated due to migrating to fork. ** | ||||
|  | ||||
| --- | ||||
|  | ||||
| # Arch Linux package | ||||
| 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/ | ||||
|   | ||||
| @@ -1,39 +1,42 @@ | ||||
| # Debian package | ||||
| ## Requirements | ||||
| - Any distribution that supports Java 8 | ||||
|  | ||||
| ## Install | ||||
| 1. Download the [latest release](https://github.com/kamax-io/mxisd/releases/latest) | ||||
| 1. Download the [latest release](https://github.com/ma1uta/ma1sd/releases/latest) | ||||
| 2. Run: | ||||
| ```bash | ||||
| dpkg -i /path/to/downloaded/mxisd.deb | ||||
| dpkg -i /path/to/downloaded/ma1sd.deb | ||||
| ``` | ||||
| ## Files | ||||
| | Location                            | Purpose                                      | | ||||
| |-------------------------------------|----------------------------------------------| | ||||
| | `/etc/mxisd`                        | Configuration directory                      | | ||||
| | `/etc/mxisd/mxisd.yaml`             | Main configuration file                      | | ||||
| | `/etc/systemd/system/mxisd.service` | Systemd configuration file for mxisd service | | ||||
| | `/usr/lib/mxisd`                    | Binaries                                     | | ||||
| | `/var/lib/mxisd`                    | Data                                         | | ||||
| | `/var/lib/mxisd/signing.key`        | Default location for mxisd signing keys      | | ||||
| | `/etc/ma1sd`                        | Configuration directory                      | | ||||
| | `/etc/ma1sd/ma1sd.yaml`             | Main configuration file                      | | ||||
| | `/etc/systemd/system/ma1sd.service` | Systemd configuration file for ma1sd service | | ||||
| | `/usr/lib/ma1sd`                    | Binaries                                     | | ||||
| | `/var/lib/ma1sd`                    | Data                                         | | ||||
| | `/var/lib/ma1sd/signing.key`        | Default location for ma1sd signing keys      | | ||||
|  | ||||
| ## Control | ||||
| Start mxisd using: | ||||
| Start ma1sd using: | ||||
| ```bash | ||||
| sudo systemctl start mxisd | ||||
| sudo systemctl start ma1sd | ||||
| ``` | ||||
|  | ||||
| Stop mxisd using: | ||||
| Stop ma1sd using: | ||||
| ```bash | ||||
| sudo systemctl stop mxisd | ||||
| sudo systemctl stop ma1sd | ||||
| ``` | ||||
|  | ||||
| ## Troubleshoot | ||||
| All logs are sent to `STDOUT` which are saved in `/var/log/syslog` by default.   | ||||
| 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: | ||||
| ``` | ||||
| journalctl -f -n 99 -u mxisd | ||||
| journalctl -f -n 99 -u ma1sd | ||||
| ``` | ||||
|   | ||||
| @@ -1,13 +1,19 @@ | ||||
| --- | ||||
|  | ||||
| ** Outdated due to migrating to fork. ** | ||||
|  | ||||
| --- | ||||
|  | ||||
| # Docker | ||||
| ## Fetch | ||||
| Pull the latest stable image: | ||||
| ```bash | ||||
| docker pull kamax/mxisd | ||||
| docker pull ma1uta/ma1sd | ||||
| ``` | ||||
|  | ||||
| ## Configure | ||||
| 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. | ||||
|  | ||||
| ## Run | ||||
| @@ -16,7 +22,10 @@ Use the following command after adapting to your needs: | ||||
| - The volumes host paths | ||||
|  | ||||
| ```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). | ||||
|   | ||||
							
								
								
									
										14
									
								
								docs/install/nixos.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								docs/install/nixos.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| --- | ||||
|  | ||||
| ** Outdated due to migrating to fork. ** | ||||
|  | ||||
| --- | ||||
|  | ||||
| # NixOS package | ||||
| mxisd is available as a NixOS package in the official repos. | ||||
|  | ||||
| It is maintained by [maximilian](https://matrix.to/#/@maximilian:transformierende-gesellschaft.org), a community member.   | ||||
|  | ||||
| Related resources: | ||||
| - [NixOS](https://nixos.org/) | ||||
| - [The module definition](https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/services/networking/mxisd.nix) | ||||
							
								
								
									
										30
									
								
								docs/install/security.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								docs/install/security.md
									
									
									
									
									
										Normal 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. | ||||
| @@ -2,37 +2,43 @@ | ||||
| ## Instructions | ||||
| Follow the [build instructions](../build.md) then: | ||||
|  | ||||
| 1. Prepare files and directories: | ||||
| ### Prepare files and directories: | ||||
| ```bash | ||||
| # Create a dedicated user | ||||
| useradd -r mxisd | ||||
| useradd -r ma1sd | ||||
|  | ||||
| # Create bin directory | ||||
| mkdir /opt/mxisd | ||||
|  | ||||
| # Create config directory and set ownership | ||||
| mkdir -p /etc/opt/mxisd | ||||
| chown -R mxisd /etc/opt/mxisd | ||||
| # Create config directory | ||||
| mkdir -p /etc/ma1sd | ||||
|  | ||||
| # Create data directory and set ownership | ||||
| mkdir -p /var/opt/mxisd | ||||
| chown -R mxisd /var/opt/mxisd | ||||
| mkdir -p /var/lib/ma1sd | ||||
| chown -R ma1sd /var/lib/ma1sd | ||||
|  | ||||
| # Copy <repo root>/build/libs/mxisd.jar to bin directory | ||||
| cp ./build/libs/mxisd.jar /opt/mxisd/ | ||||
| chown mxisd /opt/mxisd/mxisd.jar | ||||
| chmod a+x /opt/mxisd/mxisd.jar | ||||
| # Create bin directory, copy the jar and launch scriot to bin directory | ||||
| mkdir /usr/lib/ma1sd | ||||
| cp ./build/libs/ma1sd.jar /usr/lib/ma1sd/ | ||||
| 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 | ||||
| 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 | ||||
| 4. Enable service for auto-startup | ||||
|  | ||||
| ### Prepare config file | ||||
| 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 | ||||
| systemctl enable mxisd | ||||
| systemctl enable ma1sd | ||||
| ``` | ||||
| 5. Start mxisd | ||||
|  | ||||
| ### Run | ||||
| ```bash | ||||
| systemctl start mxisd | ||||
| systemctl start ma1sd | ||||
| ``` | ||||
|  | ||||
| ## Debug | ||||
| ma1sd logs to stdout, which is normally sent to `/var/log/syslog` or `/var/log/messages`. | ||||
|   | ||||
							
								
								
									
										16
									
								
								docs/migration-from-mxisd.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								docs/migration-from-mxisd.md
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										21
									
								
								docs/operations.md
									
									
									
									
									
										Normal 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. | ||||
| @@ -1,7 +1,8 @@ | ||||
| # Identity Stores | ||||
| - [Synapse](synapse.md) | ||||
| - [LDAP-based](ldap.md) | ||||
| - [SQL Databases](sql.md) | ||||
| - [Website / Web service / Web app](rest.md) | ||||
| - [Google Firebase](firebase.md) | ||||
| - [Wordpress](wordpress.md) | ||||
| - [Synapse](synapse.md) - Turn your SynapseDB into a self-contained Identity store | ||||
| - [LDAP-based](ldap.md) - Any LDAP-based product like Active Directory, Samba, NetIQ, OpenLDAP | ||||
| - [SQL Databases](sql.md) - Most common databases like MariaDB, MySQL, PostgreSQL, SQLite | ||||
| - [Website / Web service / Web app](rest.md) - Arbitrary REST endpoints | ||||
| - [Executables](exec.md) - Run arbitrary executables with configurable stdin, arguments, environment and stdout | ||||
| - [Wordpress](wordpress.md) - Connect your Wordpress-powered website DB | ||||
| - [Google Firebase](firebase.md) - Use your Firebase users (with experimental SSO support!) | ||||
|   | ||||
							
								
								
									
										506
									
								
								docs/stores/exec.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										506
									
								
								docs/stores/exec.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,506 @@ | ||||
| # Exec Identity Store | ||||
| - [Features](#features) | ||||
| - [Overview](#overview) | ||||
| - [Configuration](#configuration) | ||||
|   - [Global](#global) | ||||
|     - [Tokens](#tokens) | ||||
|   - [Executable](#executable) | ||||
|     - [Input](#input) | ||||
|     - [Output](#output) | ||||
|   - [Examples](#examples) | ||||
|   - [Per-Feature](#per-feature) | ||||
| - [Authentication](#authentication) | ||||
|   - [Tokens](#tokens-1) | ||||
|   - [Input](#input-1) | ||||
|   - [Output](#output-1) | ||||
| - [Directory](#directory) | ||||
|   - [Tokens](#tokens-2) | ||||
|   - [Input](#input-2) | ||||
|   - [Output](#output-2) | ||||
| - [Identity](#identity) | ||||
|   - [Single Lookup](#single-lookup) | ||||
|     - [Tokens](#tokens-3) | ||||
|     - [Input](#input-3) | ||||
|     - [Output](#output-3) | ||||
|   - [Bulk Lookup](#bulk-lookup) | ||||
|     - [Tokens](#tokens-4) | ||||
|     - [Input](#input-4) | ||||
|     - [Output](#output-4) | ||||
| - [Profile](#profile) | ||||
|   - [Tokens](#tokens-5) | ||||
|   - [Input](#input-5) | ||||
|   - [Output](#output-5) | ||||
|    | ||||
| --- | ||||
|  | ||||
| ## Features | ||||
| |                       Name                      | Supported | | ||||
| |-------------------------------------------------|-----------| | ||||
| | [Authentication](../features/authentication.md) | Yes       | | ||||
| | [Directory](../features/directory.md)           | Yes       | | ||||
| | [Identity](../features/identity.md)             | Yes       | | ||||
| | [Profile](../features/profile.md)               | Yes       | | ||||
|  | ||||
| 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 ma1sd, allowing you to connect any kind of logic with any executable/script. | ||||
|  | ||||
| ## Overview | ||||
| Each request can be mapping to a fully customizable command configuration.   | ||||
| The various parameters can be provided via any combination of: | ||||
| - [Standard Input](https://en.wikipedia.org/wiki/Standard_streams#Standard_input_(stdin)) | ||||
| - [Command-line arguments](https://en.wikipedia.org/wiki/Command-line_interface#Arguments) | ||||
| - [Environment variables](https://en.wikipedia.org/wiki/Environment_variable) | ||||
|  | ||||
| Each of those supports a set of customizable token which will be replaced prior to running the command, allowing to | ||||
| provide the input values in any number of ways. | ||||
|  | ||||
| Success and data will be provided via any combination of: | ||||
| - [Exit status](https://en.wikipedia.org/wiki/Exit_status) | ||||
| - [Standard Output](https://en.wikipedia.org/wiki/Standard_streams#Standard_output_(stdout)) | ||||
|  | ||||
| Each of those supports a set of configuration item to decide how to process the value and/or in which format. | ||||
|  | ||||
| All values, inputs and outputs are UTF-8 encoded. | ||||
|  | ||||
| ## Configuration | ||||
| Each feature comes with a set of possible lookup/action which is mapped to a generic configuration item block.   | ||||
| We will use the term `Executable` for each lookup/action and `Processor` for each configuration block. | ||||
|  | ||||
| ### Global | ||||
| ```yaml | ||||
| exec: | ||||
|   enabled: <boolean> | ||||
| ``` | ||||
| Enable/disable the Identity store at a global/default level. Each feature can still be individually enabled/disabled. | ||||
|  | ||||
| #### Tokens | ||||
| The following options allow to globally set tokens for value replacement across all features and processors config.   | ||||
| Not all features use all tokens, and each feature might also have its own specific tokens. See each feature documentation. | ||||
|    | ||||
| They can be set within the following scope: | ||||
|  | ||||
| ```yaml | ||||
| exec: | ||||
|   token: | ||||
|     <token>: '<value>' | ||||
| ``` | ||||
|  | ||||
| --- | ||||
|  | ||||
| The following tokens and default values are available: | ||||
| ```yaml | ||||
| localpart: '{localpart}' | ||||
| ``` | ||||
| Localpart of Matrix User IDs | ||||
|  | ||||
| ```yaml | ||||
| domain: '{domain}' | ||||
| ``` | ||||
| Domain of Matrix User IDs | ||||
|  | ||||
| ```yaml | ||||
| mxid: '{mxid}' | ||||
| ``` | ||||
| Full representation of Matrix User IDs | ||||
|  | ||||
| ```yaml | ||||
| medium: '{medium}' | ||||
| ``` | ||||
| Medium of 3PIDs | ||||
|  | ||||
| ```yaml | ||||
| address: '{address}' | ||||
| ``` | ||||
| Address of 3PIDs | ||||
|  | ||||
| ```yaml | ||||
| type: '{type}' | ||||
| ``` | ||||
| Type of query | ||||
|  | ||||
| ```yaml | ||||
| query: '{query}' | ||||
| ``` | ||||
| Query value | ||||
|  | ||||
| ### Executable | ||||
| *Executable*s have the following options: | ||||
| ```yaml | ||||
| command: '/path/to/executableOrScript' | ||||
|  | ||||
| ``` | ||||
| Set the executable (relative or absolute) path to be executed. If no command is given, the action will return a "neutral" | ||||
| result if possible or be skipped altogether. | ||||
|  | ||||
| --- | ||||
|  | ||||
| Command line arguments can be given via a list via both YAML formats: | ||||
| ```yaml | ||||
| args: | ||||
|  - '-t' | ||||
|  - '{token}' | ||||
|  - '-v' | ||||
|  - 'value' | ||||
| ``` | ||||
| or | ||||
| ```yaml | ||||
| args: ['-t', '{token}', '-v', 'value] | ||||
| ``` | ||||
| Each argument will be processed for token replacement. | ||||
|  | ||||
| --- | ||||
|  | ||||
| Environment variables can be given as key/value pairs: | ||||
| ```yaml | ||||
| env: | ||||
|   ENV_VAR_1: 'value' | ||||
|   ENV_VAR_2: '{token}' | ||||
| ```  | ||||
| Each variable value will be processed for token replacement. | ||||
|  | ||||
| #### Input | ||||
| Standard input can be configured in the namespaces `input` with: | ||||
| - `type`: The format to use | ||||
| - `template`: The full or partial template with tokens to be used when generating the input | ||||
|  | ||||
| Not all features and *Executable*s allow for a template to be provided.   | ||||
| Templates for listed-based input are not supported at this time.   | ||||
| Default templates may be provided per *Executable*. | ||||
|  | ||||
| The following types are available: | ||||
| - `json`: Use JSON format, shared with the [REST Identity Store](rest.md) | ||||
| - `plain`: Use a custom multi-lines, optionally tab-separated input | ||||
|  | ||||
| #### Output | ||||
| Standard output can be configured in the namespaces `output` with: | ||||
| - `type`: The format to use | ||||
| - `template`: The full or partial template with tokens to be used when processing the output | ||||
|  | ||||
| Not all features and *Executable*s allow for a template to be provided.   | ||||
| Templates for listed-based output are not supported at this time.   | ||||
| Default templates may be provided per *Executable*. | ||||
|  | ||||
| The following types are available: | ||||
| - `json`: Use JSON format, shared with the [REST Identity Store](rest.md) | ||||
| - `plain`: Use a custom multi-lines, optionally tab-separated output | ||||
|  | ||||
| ### Examples | ||||
| #### Basic | ||||
| ```yaml | ||||
| exec: | ||||
|   auth: | ||||
|     enabled: true | ||||
|     command: '/opt/ma1sd-exec/auth.sh' | ||||
|     args: ['{localpart}'] | ||||
|     input: | ||||
|       type: 'plain' | ||||
|       template: '{password}' | ||||
|   env: | ||||
|     DOMAIN: '{domain}' | ||||
| ``` | ||||
| With Authentication enabled, run `/opt/ma1sd-exec/auth.sh` when validating credentials, providing: | ||||
| - 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 single environment variable `DOMAIN` containing Matrix ID domain, if given | ||||
|  | ||||
| The command will use the default values for: | ||||
| - Success exit status of `0` | ||||
| - Failure exit status of `1` | ||||
| - Any other exit status considered as error | ||||
| - Standard output will not be processed | ||||
|  | ||||
| #### Advanced | ||||
| Given the fictional `placeholder` feature: | ||||
| ```yaml | ||||
| exec: | ||||
|   enabled: true | ||||
|   token: | ||||
|     mxid: '{matrixId}' | ||||
|   auth: | ||||
|     token: | ||||
|       localpart: '{username}' | ||||
|     command: '/path/to/executable' | ||||
|     args: | ||||
|       - '-u' | ||||
|       - '{username}' | ||||
|     env: | ||||
|       MATRIX_DOMAIN: '{domain}' | ||||
|       MATRIX_USER_ID: '{matrixId}' | ||||
|     output: | ||||
|       type: 'json' | ||||
|     exit: | ||||
|       success: | ||||
|         - 0 | ||||
|         - 128 | ||||
|       failure: | ||||
|         - 1 | ||||
|         - 129 | ||||
| ``` | ||||
| With: | ||||
| - The Identity store enabled for all features | ||||
| - A global specific token `{matrixId}` for Matrix User IDs, replacing the default `{mxid}` | ||||
|  | ||||
| Running `/path/to/executable` providing: | ||||
| - A custom token for localpart, `{username}`, used as a 2nd command-line argument | ||||
| - An extracted Matrix User ID `localpart` provided as the second command line argument, the first one being `-u`  | ||||
| - A password, the extracted Matrix `domain` and the full User ID as arbitrary environment variables, respectively | ||||
|   `PASSWORD`, `MATRIX_DOMAIN` and `MATRIX_USER_ID` | ||||
|  | ||||
| After execution: | ||||
| - Process stdout as [JSON](https://en.wikipedia.org/wiki/JSON) | ||||
| - Consider exit status `0` and `128` as success and try to process the stdout for data | ||||
| - Consider exit status `1` and `129` as failure and try to process the stdout for error code and message | ||||
|  | ||||
| ### Per Feature | ||||
| See each dedicated [Feature](#features) section. | ||||
|  | ||||
| ## Authentication | ||||
| The Authentication feature can be enabled/disabled using: | ||||
| ```yaml | ||||
| exec: | ||||
|   auth: | ||||
|     enabled: <true/false> | ||||
| ``` | ||||
|  | ||||
| --- | ||||
|  | ||||
| This feature provides a single *Executable* under the namespace: | ||||
| ```yaml | ||||
| exec: | ||||
|   auth: | ||||
|   ... | ||||
| ``` | ||||
|  | ||||
| ### Tokens | ||||
| The following tokens/default values are specific to this feature: | ||||
| ```yaml | ||||
| password: '{password}' | ||||
| ``` | ||||
| The provided password | ||||
|  | ||||
| ### Input | ||||
| Supported input types and default templates: | ||||
|  | ||||
| #### JSON (`json`) | ||||
| Same as the [REST Identity Store](rest.md); | ||||
|  | ||||
| #### Plain (`plain`) | ||||
| Default template: | ||||
| ``` | ||||
| {localpart} | ||||
| {domain} | ||||
| {mxid} | ||||
| {password} | ||||
| ``` | ||||
|  | ||||
| ### Output | ||||
| Supported output types and default templates: | ||||
|  | ||||
| #### JSON (`json`) | ||||
| Same as the [REST Identity Store](rest.md); | ||||
|  | ||||
| #### Plain (`plain`) | ||||
| **NOTE:** This has limited support. Use the JSON type for full support. | ||||
|  | ||||
| Default template: | ||||
| ``` | ||||
| [success status, true or 1 are interpreted as success] | ||||
| [display name of the user] | ||||
| ``` | ||||
|  | ||||
| ## Directory | ||||
| The Directory feature can be enabled/disabled using: | ||||
| ```yaml | ||||
| exec: | ||||
|   directory: | ||||
|     enabled: <true/false> | ||||
| ``` | ||||
|  | ||||
| --- | ||||
|  | ||||
| Two search types configuration namespace are available, using the same input/output formats and templates: | ||||
|  | ||||
| By name: | ||||
| ```yaml | ||||
| exec: | ||||
|   directory: | ||||
|     search: | ||||
|       byName: | ||||
|         ... | ||||
| ``` | ||||
| By 3PID: | ||||
| ```yaml | ||||
| exec: | ||||
|   directory: | ||||
|     search: | ||||
|       byThreepid: | ||||
|         ... | ||||
| ``` | ||||
|  | ||||
| #### Tokens | ||||
| No specific tokens are available. | ||||
|  | ||||
| #### Input | ||||
| Supported input types and default templates: | ||||
|  | ||||
| ##### JSON (`json`) | ||||
| Same as the [REST Identity Store](rest.md); | ||||
|  | ||||
| ##### Plain (`plain`) | ||||
| Default template: | ||||
| ``` | ||||
| [type of search, following the REST Identity store format] | ||||
| [query string] | ||||
| ``` | ||||
|  | ||||
| #### Output | ||||
| Supported output types and default templates: | ||||
|  | ||||
| ##### JSON (`json`) | ||||
| Same as the [REST Identity Store](rest.md); | ||||
|  | ||||
| ##### Plain (`plain`) | ||||
| **Not supported at this time.** Use the JSON type. | ||||
|  | ||||
| ## Identity | ||||
| The Identity feature can be enabled/disabled using: | ||||
| ```yaml | ||||
| exec.identity.enabled: <true/false> | ||||
| ``` | ||||
|  | ||||
| ### Single lookup | ||||
| Configuration namespace: | ||||
| ```yaml | ||||
| exec.identity.lookup.single: | ||||
|   ... | ||||
| ``` | ||||
|  | ||||
| #### Tokens | ||||
| No specific tokens are available. | ||||
|  | ||||
| #### Input | ||||
| Supported input types and default templates: | ||||
|  | ||||
| ##### JSON (`json`) | ||||
| Same as the [REST Identity Store](rest.md); | ||||
|  | ||||
| ##### Plain (`plain`) | ||||
| Default template: | ||||
| ``` | ||||
| {medium} | ||||
| {address} | ||||
| ``` | ||||
|  | ||||
| #### Output | ||||
| Supported output types and default templates: | ||||
|  | ||||
| ##### JSON (`json`) | ||||
| Same as the [REST Identity Store](rest.md); | ||||
|  | ||||
| ##### Plain (`plain`) | ||||
| Default template: | ||||
| ``` | ||||
| [User ID type, as documented in the REST Identity Store] | ||||
| [User ID value] | ||||
| ``` | ||||
|  | ||||
| The User ID type will default to `localpart` if: | ||||
| - Only one line is returned | ||||
| - The first line is empty | ||||
|  | ||||
| ### Bulk lookup | ||||
| Configuration namespace: | ||||
| ```yaml | ||||
| exec: | ||||
|   identity: | ||||
|     lookup: | ||||
|       bulk: | ||||
|         ... | ||||
| ``` | ||||
|  | ||||
| #### Tokens | ||||
| No specific tokens are available. | ||||
|  | ||||
| #### Input | ||||
| Supported input types and default templates: | ||||
|  | ||||
| ##### JSON (`json`) | ||||
| **NOTE:** Custom Templates are not supported. | ||||
|  | ||||
| Same as the [REST Identity Store](rest.md). | ||||
|  | ||||
| ##### Plain (`plain`) | ||||
| **Not supported at this time.** Use the JSON type. | ||||
|  | ||||
| #### Output | ||||
| Supported output types and default templates: | ||||
|  | ||||
| ##### JSON (`json`) | ||||
| **NOTE:** Custom Templates are not supported. | ||||
|  | ||||
| Same as the [REST Identity Store](rest.md). | ||||
|  | ||||
| ##### Plain (`plain`) | ||||
| **Not supported at this time.** Use the JSON type. | ||||
|  | ||||
| ## Profile | ||||
| The Profile feature can be enabled/disabled using: | ||||
| ```yaml | ||||
| exec: | ||||
|   profile: | ||||
|     enabled: <true/false> | ||||
| ``` | ||||
|  | ||||
| --- | ||||
|  | ||||
| The following *Executable*s namespace are available, share the same input/output formats and templates: | ||||
|  | ||||
| Get Display name: | ||||
| ```yaml | ||||
| exec: | ||||
|   profile: | ||||
|     displayName: | ||||
|       ... | ||||
| ``` | ||||
|  | ||||
| Get 3PIDs: | ||||
| ```yaml | ||||
| exec: | ||||
|   profile: | ||||
|     threePid: | ||||
|       ... | ||||
| ``` | ||||
|  | ||||
| Get Roles: | ||||
| ```yaml | ||||
| exec: | ||||
|   profile: | ||||
|     role: | ||||
|       ... | ||||
| ``` | ||||
|  | ||||
|  | ||||
| ### Tokens | ||||
| No specific tokens are available. | ||||
|  | ||||
| ### Input | ||||
| Supported input types and default templates: | ||||
|  | ||||
| #### JSON (`json`) | ||||
| Same as the [REST Identity Store](rest.md); | ||||
|  | ||||
| #### Plain (`plain`) | ||||
| Default template: | ||||
| ``` | ||||
| {localpart} | ||||
| {domain} | ||||
| {mxid} | ||||
| ``` | ||||
| ### Output | ||||
| Supported output types and default templates: | ||||
|  | ||||
| #### JSON (`json`) | ||||
| Same as the [REST Identity Store](rest.md); | ||||
|  | ||||
| #### Plain (`plain`) | ||||
| **Not supported at this time.** Use the JSON type. | ||||
| @@ -2,11 +2,12 @@ | ||||
| https://firebase.google.com/ | ||||
|  | ||||
| ## Features | ||||
| |      Name      | Supported? | | ||||
| |----------------|------------| | ||||
| | Authentication | Yes        | | ||||
| | Directory      | No         | | ||||
| | Identity       | Yes        | | ||||
| |                       Name                      | Supported | | ||||
| |-------------------------------------------------|-----------| | ||||
| | [Authentication](../features/authentication.md) | Yes       | | ||||
| | [Directory](../features/directory.md)           | No        | | ||||
| | [Identity](../features/identity.md)             | Yes       | | ||||
| | [Profile](../features/profile.md)               | No        | | ||||
|  | ||||
| ## Requirements | ||||
| This backend requires a suitable Matrix client capable of performing Firebase authentication and passing the following | ||||
| @@ -18,35 +19,41 @@ If your client is Riot, you will need a custom version. | ||||
|  | ||||
| ## Configuration | ||||
| ```yaml | ||||
| firebase.enabled: <boolean> | ||||
| firebase: | ||||
|   enabled: <boolean> | ||||
| ``` | ||||
| Enable/disable this identity store. | ||||
|  | ||||
| Example: | ||||
| ```yaml | ||||
| firebase.enabled: <boolean> | ||||
| firebase: | ||||
|   enabled: <boolean> | ||||
| ``` | ||||
|  | ||||
| --- | ||||
|  | ||||
| ```yaml | ||||
| firebase.credentials: <string> | ||||
| firebase: | ||||
|   credentials: <string> | ||||
| ``` | ||||
| Path to the credentials file provided by Google Firebase to use with an external app. | ||||
|  | ||||
| Example: | ||||
| ```yaml | ||||
| firebase.credentials: '/path/to/firebase/credentials.json' | ||||
| firebase: | ||||
|   credentials: '/path/to/firebase/credentials.json' | ||||
| ``` | ||||
|  | ||||
| --- | ||||
|  | ||||
| ```yaml | ||||
| firebase.database: <string> | ||||
| firebase: | ||||
|   database: <string> | ||||
| ``` | ||||
| URL to your Firebase database. | ||||
|  | ||||
| Example: | ||||
| ```yaml | ||||
| firebase.database: 'https://my-project.firebaseio.com/' | ||||
| firebase: | ||||
|   database: 'https://my-project.firebaseio.com/' | ||||
| ``` | ||||
|   | ||||
| @@ -8,36 +8,45 @@ | ||||
| For NetIQ, replace all the `ldap` prefix in the configuration by `netiq`. | ||||
|  | ||||
| ## Features | ||||
| |      Name      | Supported? | | ||||
| |----------------|------------| | ||||
| | Authentication | Yes        | | ||||
| | Directory      | Yes        | | ||||
| | Identity       | Yes        | | ||||
| |                       Name                      | Supported | | ||||
| |-------------------------------------------------|-----------| | ||||
| | [Authentication](../features/authentication.md) | Yes       | | ||||
| | [Directory](../features/directory.md)           | Yes       | | ||||
| | [Identity](../features/identity.md)             | Yes       | | ||||
| | [Profile](../features/profile.md)               | Yes       | | ||||
|  | ||||
| ## Getting started | ||||
| ### 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 | ||||
| ldap.enabled: true | ||||
| ldap.connection.host: 'ldapHostnameOrIp' | ||||
| ldap.connection.port: 389 | ||||
| ldap.connection.bindDn: 'CN=My Mxisd User,OU=Users,DC=example,DC=org' | ||||
| ldap.connection.bindPassword: 'TheUserPassword' | ||||
| ldap.connection.baseDn: 'OU=Users,DC=example,DC=org' | ||||
| ldap: | ||||
|   enabled: true | ||||
|   connection: | ||||
|     host: 'ldapHostnameOrIp' | ||||
|     port: 389 | ||||
|     bindDn: 'CN=My User,OU=Users,DC=example,DC=org' | ||||
|     bindPassword: 'TheUserPassword' | ||||
|     baseDNs: | ||||
|       - '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`. | ||||
|  | ||||
| ### TLS/SSL connection | ||||
| If you would like to use a TLS/SSL connection, use the following configuration options (STARTLS not supported): | ||||
| ```yaml | ||||
| ldap.connection.tls: true | ||||
| ldap.connection.port: 12345 | ||||
| ldap: | ||||
|   connection: | ||||
|     tls: true | ||||
|     port: 12345 | ||||
| ``` | ||||
|  | ||||
| ### Filter results | ||||
| You can also set a default global filter on any LDAP queries: | ||||
| ```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 can be overwritten or append in each specific flow describe below. | ||||
| @@ -60,8 +69,11 @@ most certainly configure those mappings. | ||||
|  | ||||
| The following example would set the `sAMAccountName` attribute as a Matrix User ID localpart: | ||||
| ```yaml | ||||
| ldap.attribute.uid.type: 'uid' | ||||
| ldap.attribute.uid.value: 'sAMAccountName' | ||||
| ldap: | ||||
|   attribute: | ||||
|     uid: | ||||
|       type: 'uid' | ||||
|       value: 'sAMAccountName' | ||||
| ``` | ||||
|  | ||||
| #### Display name | ||||
| @@ -69,20 +81,24 @@ Use `ldap.attribute.name`. | ||||
|  | ||||
| The following example would set the display name to the value of the `cn` attribute: | ||||
| ```yaml | ||||
| ldap.attribute.name: 'cn' | ||||
| ldap: | ||||
|   attribute: | ||||
|     name: 'cn' | ||||
| ``` | ||||
|  | ||||
| #### 3PIDs | ||||
| You can also change the attribute lists for 3PID, like email or phone numbers. | ||||
|  | ||||
| The following example would overwrite the [default list of attributes](../../src/main/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: | ||||
| ```yaml | ||||
| ldap.attribute.threepid.email: | ||||
| ldap: | ||||
|   attribute: | ||||
|     threepid: | ||||
|       email: | ||||
|         - 'mail' | ||||
|         - 'otherMailAttribute' | ||||
|  | ||||
| ldap.attribute.threepid.msisdn: | ||||
|       msisdn: | ||||
|         - 'phone' | ||||
|         - 'otherPhoneAttribute' | ||||
| ``` | ||||
| @@ -97,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. | ||||
|  | ||||
| ### 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 | ||||
| options to get a lit of attributes to be used to build the user profile to pass on to synapse during authentication. | ||||
|  | ||||
| #### 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 | ||||
| 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 | ||||
| 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: | ||||
| ```yaml | ||||
| ldap.directory.attribute.other: | ||||
| ldap: | ||||
|   directory: | ||||
|     attribute: | ||||
|       other: | ||||
|         - 'myNicknameAttribute' | ||||
|         - 'memberOf' | ||||
|         - 'employeeNumberAttribute' | ||||
|   | ||||
| @@ -3,37 +3,45 @@ The REST backend allows you to query identity data in existing webapps, like: | ||||
| - Forums (phpBB, Discourse, etc.) | ||||
| - Custom Identity stores (Keycloak, ...) | ||||
| - CRMs (Wordpress, ...) | ||||
| - self-hosted clouds (Nextcloud, ownCloud, ...) | ||||
| - Self-hosted clouds (Nextcloud, ownCloud, ...) | ||||
|  | ||||
| To integrate this backend with your webapp, you will need to implement three specific REST endpoints detailed below. | ||||
| To integrate this backend with your webapp, you will need to implement the REST endpoints described below. | ||||
|  | ||||
| ## Features | ||||
| | Name                                            | Supported? | | ||||
| |----------------|------------| | ||||
| | Authentication | Yes        | | ||||
| | Directory      | Yes        | | ||||
| | Identity       | Yes        | | ||||
| |-------------------------------------------------|------------| | ||||
| | [Authentication](../features/authentication.md) | Yes        | | ||||
| | [Directory](../features/directory.md)           | Yes        | | ||||
| | [Identity](../features/identity.md)             | Yes        | | ||||
| | [Profile](../features/profile.md)               | Yes        | | ||||
|  | ||||
| ## Configuration | ||||
| | Key                                  | Default                                        | Description                                          | | ||||
| |----------------------------------|------------------------------------------------|------------------------------------------------------| | ||||
| |--------------------------------------|------------------------------------------------|------------------------------------------------------| | ||||
| | `rest.enabled`                       | `false`                                        | Globally enable/disable the REST backend             | | ||||
| | `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.directory`       | `/_mxisd/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.bulk`   | `/_mxisd/backend/api/v1/identity/bulk`         | Endpoint to query a list of 3PID                     | | ||||
| | `rest.endpoints.auth`                | `/_ma1sd/backend/api/v1/auth/login`            | Validate credentials and get user profile            | | ||||
| | `rest.endpoints.directory`           | `/_ma1sd/backend/api/v1/directory/user/search` | Search for users by arbitrary input                  | | ||||
| | `rest.endpoints.identity.single`     | `/_ma1sd/backend/api/v1/identity/single`       | Endpoint to query a single 3PID                      | | ||||
| | `rest.endpoints.identity.bulk`       | `/_ma1sd/backend/api/v1/identity/bulk`         | Endpoint to query a list of 3PID                     | | ||||
| | `rest.endpoints.profile.displayName` | `/_ma1sd/backend/api/v1/profile/displayName`   | Query the display name 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`       | `/_ma1sd/backend/api/v1/profile/roles`         | Query the Roles for a Matrix ID | ||||
|  | ||||
| Endpoint values can handle two formats: | ||||
| - URL Path starting with `/` that gets happened to the `rest.host` | ||||
| - Full URL, if you want each endpoint to go to a specific server/protocol/port | ||||
|  | ||||
| If an endpoint value is configured as an empty string, it will disable that specific feature, essentially bypassing the | ||||
| Identity store for that specific query. | ||||
|  | ||||
| `rest.host` is mandatory if at least one endpoint is not a full URL. | ||||
|  | ||||
| ## Endpoints | ||||
| ### Authentication | ||||
| HTTP method: `POST`   | ||||
| Content-type: JSON UTF-8 | ||||
| - Method: `POST` | ||||
| - Content-Type: `application/json` (JSON) | ||||
| - Encoding: `UTF8` | ||||
|    | ||||
| #### Request Body | ||||
| ```json | ||||
| @@ -86,8 +94,9 @@ If the authentication succeed: | ||||
| ``` | ||||
|  | ||||
| ### Directory | ||||
| HTTP method: `POST` | ||||
| Content-type: JSON UTF-8 | ||||
| - Method: `POST` | ||||
| - Content-Type: `application/json` (JSON) | ||||
| - Encoding: `UTF8` | ||||
|  | ||||
| #### Request Body | ||||
| ```json | ||||
| @@ -112,7 +121,7 @@ If users found: | ||||
|       "user_id": "UserIdLocalpart" | ||||
|     }, | ||||
|     { | ||||
|       ... | ||||
|       "...": "..." | ||||
|     } | ||||
|   ] | ||||
| } | ||||
| @@ -128,10 +137,11 @@ If no user found: | ||||
|  | ||||
| ### Identity | ||||
| #### Single 3PID lookup | ||||
| HTTP method: `POST`   | ||||
| Content-type: JSON UTF-8   | ||||
| - Method: `POST` | ||||
| - Content-Type: `application/json` (JSON) | ||||
| - Encoding: `UTF8` | ||||
|    | ||||
| #### Request Body | ||||
| ##### Request Body | ||||
| ```json | ||||
| { | ||||
|   "lookup": { | ||||
| @@ -141,7 +151,7 @@ Content-type: JSON UTF-8 | ||||
| } | ||||
| ``` | ||||
|  | ||||
| #### Response Body | ||||
| ##### Response Body | ||||
| If a match was found: | ||||
| - `lookup.id.type` supported values: `localpart`, `mxid` | ||||
| ```json | ||||
| @@ -163,10 +173,11 @@ If no match was found: | ||||
| ``` | ||||
|  | ||||
| #### Bulk 3PID lookup | ||||
| HTTP method: `POST`   | ||||
| Content-type: JSON UTF-8   | ||||
| - Method: `POST` | ||||
| - Content-Type: `application/json` (JSON) | ||||
| - Encoding: `UTF8` | ||||
|    | ||||
| #### Request Body | ||||
| ##### Request Body | ||||
| ```json | ||||
| { | ||||
|   "lookup": [ | ||||
| @@ -182,7 +193,7 @@ Content-type: JSON UTF-8 | ||||
| } | ||||
| ``` | ||||
|  | ||||
| #### Response Body | ||||
| ##### Response Body | ||||
| For all entries where a match was found: | ||||
| - `lookup[].id.type` supported values: `localpart`, `mxid` | ||||
| ```json | ||||
| @@ -214,3 +225,53 @@ If no match was found: | ||||
|   "lookup": [] | ||||
| } | ||||
| ``` | ||||
|  | ||||
| ### Profile | ||||
| #### Request Body | ||||
| For all requests, the values are the same: | ||||
| - Method: `POST` | ||||
| - Content-Type: `application/json` (JSON) | ||||
| - Encoding: `UTF8` | ||||
|  | ||||
| With body (example values): | ||||
| ##### Request Body | ||||
| ```json | ||||
| { | ||||
|     "mxid": "@john.doe:example.org", | ||||
|     "localpart": "john.doe", | ||||
|     "domain": "example.org" | ||||
| } | ||||
| ``` | ||||
| #### Response Body | ||||
| For all responses, the same object structure will be parsed, making the non-relevant fields as optional. | ||||
|  | ||||
| Structure with example values: | ||||
| ```json | ||||
| { | ||||
|   "profile": { | ||||
|     "display_name": "John Doe", | ||||
|     "threepids": [ | ||||
|       { | ||||
|         "medium": "email", | ||||
|         "address": "john.doe@example.org" | ||||
|       }, | ||||
|       { | ||||
|         "...": "..." | ||||
|       } | ||||
|     ], | ||||
|     "roles": [ | ||||
|       "DomainUsers", | ||||
|       "SalesOrg", | ||||
|       "..." | ||||
|     ] | ||||
|   } | ||||
| } | ||||
| ``` | ||||
| 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": {} | ||||
| } | ||||
| ``` | ||||
|   | ||||
| @@ -6,27 +6,30 @@ | ||||
| - SQLite | ||||
|  | ||||
| ## Features | ||||
| |      Name      | Supported? | | ||||
| |----------------|------------| | ||||
| | Authentication | No         | | ||||
| | Directory      | Yes        | | ||||
| | Identity       | Yes        | | ||||
| |                       Name                      | Supported | | ||||
| |-------------------------------------------------|-----------| | ||||
| | [Authentication](../features/authentication.md) | No        | | ||||
| | [Directory](../features/directory.md)           | Yes       | | ||||
| | [Identity](../features/identity.md)             | Yes       | | ||||
| | [Profile](../features/profile.md)               | Yes       | | ||||
|  | ||||
| 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 | ||||
| the [REST Identity store](rest.md). | ||||
| the [Exec Identity Store](exec.md) or the [REST Identity Store](rest.md). | ||||
|  | ||||
| ## Configuration | ||||
| ### Basic | ||||
| ```yaml | ||||
| sql.enabled: <boolean> | ||||
| sql: | ||||
|   enabled: <boolean> | ||||
| ``` | ||||
| Enable/disable the identity store | ||||
|  | ||||
| --- | ||||
|  | ||||
| ```yaml | ||||
| sql.type: <string> | ||||
| sql: | ||||
|   type: <string> | ||||
| ``` | ||||
| Set the SQL backend to use: | ||||
| - `sqlite` | ||||
| @@ -37,14 +40,16 @@ Set the SQL backend to use: | ||||
| ### Connection | ||||
| #### SQLite | ||||
| ```yaml | ||||
| sql.connection: <string> | ||||
| sql: | ||||
|   connection: <string> | ||||
| ``` | ||||
| Set the value to the absolute path to the Synapse SQLite DB file. | ||||
| Example: `/path/to/sqlite/file.db` | ||||
|  | ||||
| #### Others | ||||
| ```yaml | ||||
| sql.connection: //<HOST[:PORT]/DB?username=USER&password=PASS | ||||
| sql: | ||||
|   connection: //<HOST[:PORT]/DB?user=USER&password=PASS | ||||
| ``` | ||||
| Set the connection info for the database by replacing the following values: | ||||
| - `HOST`: Hostname of the SQL server | ||||
| @@ -57,14 +62,17 @@ This follow the JDBC URI syntax. See [official website](https://docs.oracle.com/ | ||||
|  | ||||
| ### Directory | ||||
| ```yaml | ||||
| sql.directory.enabled: false | ||||
| sql: | ||||
|   directory: | ||||
|     enabled: false | ||||
| ``` | ||||
|  | ||||
|  | ||||
| --- | ||||
|  | ||||
| ```yaml | ||||
| sql.directory.query: | ||||
| sql: | ||||
|   directory: | ||||
|     query: | ||||
|       name: | ||||
|         type: <string> | ||||
|         value: <string> | ||||
| @@ -72,7 +80,7 @@ sql.directory.query: | ||||
|         type: <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 | ||||
| - `mxid` will use the ID as-is. If it is not a valid Matrix ID, the search will fail. | ||||
|  | ||||
| @@ -82,7 +90,9 @@ For each query, `type` can be used to tell mxisd how to process the ID column: | ||||
|  | ||||
| Example: | ||||
| ```yaml | ||||
| sql.directory.query: | ||||
| sql: | ||||
|   directory: | ||||
|     query: | ||||
|       name: | ||||
|         type: 'localpart' | ||||
|         value: 'SELECT idColumn, displayNameColumn FROM table WHERE displayNameColumn LIKE ?' | ||||
| @@ -92,7 +102,41 @@ sql.directory.query: | ||||
| ``` | ||||
|  | ||||
| ### 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 | ||||
| sql.identity.type: <string> | ||||
| sql.identity.query: <string> | ||||
| sql: | ||||
|   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. | ||||
|   | ||||
| @@ -1,26 +1,31 @@ | ||||
| # 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 | ||||
| |      Name      | Supported? | | ||||
| |----------------|------------| | ||||
| | Authentication | No         | | ||||
| | Directory      | Yes        | | ||||
| | Identity       | Yes        | | ||||
| |                       Name                      | Supported | | ||||
| |-------------------------------------------------|-----------| | ||||
| | [Authentication](../features/authentication.md) | No        | | ||||
| | [Directory](../features/directory.md)           | Yes       | | ||||
| | [Identity](../features/identity.md)             | 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 | ||||
| ### Basic | ||||
| ```yaml | ||||
| synapseSql.enabled: <boolean> | ||||
| synapseSql: | ||||
|   enabled: <boolean> | ||||
| ``` | ||||
| Enable/disable the identity store | ||||
|  | ||||
| --- | ||||
|  | ||||
| ```yaml | ||||
| synapseSql.type: <string> | ||||
| synapseSql: | ||||
|   type: <string> | ||||
| ``` | ||||
| Set the SQL backend to use which is configured in synapse: | ||||
| - `sqlite` | ||||
| @@ -28,14 +33,16 @@ Set the SQL backend to use which is configured in synapse: | ||||
|  | ||||
| ### SQLite | ||||
| ```yaml | ||||
| synapseSql.connection: <string> | ||||
| synapseSql: | ||||
|   connection: <string> | ||||
| ``` | ||||
| Set the value to the absolute path to the Synapse SQLite DB file. | ||||
| Example: `/path/to/synapse/sqliteFile.db` | ||||
|  | ||||
| ### PostgreSQL | ||||
| ```yaml | ||||
| synapseSql.connection: //<HOST[:PORT]/DB?username=USER&password=PASS | ||||
| synapseSql: | ||||
|   connection: //<HOST[:PORT]/DB?user=USER&password=PASS | ||||
| ``` | ||||
| Set the connection info for the database by replacing the following values: | ||||
| - `HOST`: Hostname of the SQL server | ||||
|   | ||||
| @@ -5,11 +5,12 @@ Two types of connections are required for full support: | ||||
| - Direct SQL access | ||||
|  | ||||
| ## Features | ||||
| |      Name      | Supported? | | ||||
| |----------------|------------| | ||||
| | Authentication | Yes        | | ||||
| | Directory      | Yes        | | ||||
| | Identity       | Yes        | | ||||
| |                       Name                      | Supported | | ||||
| |-------------------------------------------------|-----------| | ||||
| | [Authentication](../features/authentication.md) | Yes       | | ||||
| | [Directory](../features/directory.md)           | Yes       | | ||||
| | [Identity](../features/identity.md)             | Yes       | | ||||
| | [Profile](../features/profile.md)               | No        | | ||||
|  | ||||
| ## Requirements | ||||
| - [Wordpress](https://wordpress.org/download/) >= 4.4 | ||||
| @@ -28,30 +29,47 @@ define('JWT_AUTH_SECRET_KEY', 'your-top-secret-key'); | ||||
|  | ||||
| #### Rewrite of `index.php` | ||||
| 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: | ||||
| ```yaml | ||||
| wordpress.enabled: true | ||||
| wordpress: | ||||
|   enabled: true | ||||
| ``` | ||||
| Configure the URL to your Wordpress installation - see above about added `/index.php`: | ||||
| ```yaml | ||||
| wordpress.rest.base: 'http://localhost:8080' | ||||
| wordpress: | ||||
|   rest: | ||||
|     base: 'http://localhost:8080' | ||||
| ``` | ||||
| Configure the SQL connection to your Wordpress database: | ||||
| ```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: | ||||
| ```yaml | ||||
| wordpress.sql.type: <string> | ||||
| wordpress: | ||||
|   sql: | ||||
|     type: <string> | ||||
| ``` | ||||
| With possible values: | ||||
| - `mysql` | ||||
| - `mariadb` | ||||
| - `postgresql` | ||||
| - `sqlite` | ||||
|  | ||||
| --- | ||||
|  | ||||
| To configure the tables prefix for default queries, in case a custom value was set during Wordpress install: | ||||
| ```yaml | ||||
| wordpress: | ||||
|   sql: | ||||
|     tablePrefix: <string> | ||||
| ``` | ||||
| By default, the value is set to `wp_`. | ||||
|   | ||||
| @@ -1,19 +1,19 @@ | ||||
| # Email notifications - SMTP connector | ||||
| Enabled by default. | ||||
|  | ||||
| Connector ID: `smtp` | ||||
|  | ||||
| ## Configuration | ||||
| ```yaml | ||||
| threepid.medium.email: | ||||
| threepid: | ||||
|   medium: | ||||
|     email: | ||||
|       identity: | ||||
|         from: 'identityServerEmail@example.org' | ||||
|         name: 'My Identity Server' | ||||
|       connectors: | ||||
|         smtp: | ||||
|           host: 'smtpHostname' | ||||
|       port: 587 | ||||
|       tls: 1 # 0 = no STARTLS, 1 = try, 2 = force | ||||
|           tls: 1 # 0 = no STARTLS, 1 = try, 2 = force, 3 = TLS/SSL | ||||
|           port: 587 # Set appropriate value depending on your TLS setting | ||||
|           login: 'smtpLogin' | ||||
|           password: 'smtpPassword' | ||||
| ``` | ||||
|   | ||||
| @@ -1,11 +1,14 @@ | ||||
| # SMS notifications - Twilio connector | ||||
| Enabled by default. | ||||
|  | ||||
| Connector ID: `twilio` | ||||
|  | ||||
| ## Configuration | ||||
| ```yaml | ||||
| threepid.medium.msisdn.connectors.twilio.accountSid: 'myAccountSid' | ||||
| threepid.medium.msisdn.connectors.twilio.authToken: 'myAuthToken' | ||||
| threepid.medium.msisdn.connectors.twilio.number: '+123456789' | ||||
| threepid: | ||||
|   medium: | ||||
|     msisdn: | ||||
|       connectors: | ||||
|         twilio: | ||||
|           account_sid: 'myAccountSid' | ||||
|           auth_token: 'myAuthToken' | ||||
|           number: '+123456789' | ||||
| ``` | ||||
|   | ||||
| @@ -18,13 +18,16 @@ This handler can be used with the 3PID types: | ||||
| ## Configuration | ||||
| Enabled by default or with: | ||||
| ```yaml | ||||
| notification.handler.email: 'raw' | ||||
| notification: | ||||
|   handler: | ||||
|     email: 'raw' | ||||
| ``` | ||||
|  | ||||
| **WARNING:** Will be consolidated soon, prone to breaking changes.   | ||||
| Structure and default values: | ||||
| ```yaml | ||||
| threepid.medium: | ||||
| threepid: | ||||
|   medium: | ||||
|     email: | ||||
|       identity: | ||||
|         from: '' | ||||
|   | ||||
| @@ -1,7 +1,39 @@ | ||||
| # SendGrid Notification handler | ||||
| To be completed. See [raw possible configuration items](https://github.com/kamax-io/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 | ||||
| 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> | ||||
| ```  | ||||
|   | ||||
| @@ -1,71 +1,113 @@ | ||||
| # Notifications: Generate from templates | ||||
| To create notification content, you can use the `template` generator if supported for the 3PID medium which will read | ||||
| content from configured files. | ||||
| # Notifications: Template generator | ||||
| Most of the Identity actions will trigger a notification of some kind, informing the user of some confirmation, next step | ||||
| 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 | ||||
| the 3PID that was requested, the domain of your Identity server, etc. | ||||
| Those notifications are by default generated from templates and by replacing placeholder tokens in them with the relevant | ||||
| 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 | ||||
| placeholders and also have their own individual set of placeholders. | ||||
| Templates for the following events/actions are available: | ||||
| - [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 | ||||
| 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: | ||||
| ```yaml | ||||
| threepid.medium.<YOUR 3PID MEDIUM HERE>: | ||||
| threepid: | ||||
|   medium: | ||||
|     email: | ||||
|       generators: | ||||
|         template: | ||||
|           invite: '/path/to/invite-template.eml' | ||||
|           session: | ||||
|         validation: | ||||
|           local: '/path/to/validate-local-template.eml' | ||||
|           remote: 'path/to/validate-remote-template.eml' | ||||
|             validation: '/path/to/validate-template.eml' | ||||
|             unbind: | ||||
|               notification: '/path/to/unbind-notification-template.eml' | ||||
|           generic: | ||||
|             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. | ||||
|  | ||||
| ##  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. | | ||||
| In this configuration, a custom template is used for each event and a static value for the `REGISTER_URL` is set. | ||||
|   | ||||
| @@ -1,86 +1,42 @@ | ||||
| # 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 | ||||
| Pseudo-configuration to illustrate the structure: | ||||
| ```yaml | ||||
| # CONFIGURATION EXAMPLE | ||||
| # DO NOT COPY/PASTE THIS IN YOUR CONFIGURATION | ||||
| view.session.local: | ||||
| view: | ||||
|   session: | ||||
|     onTokenSubmit: | ||||
|     success: '/path/to/session/local/tokenSubmitSuccess-page.html' | ||||
|     failure: '/path/to/session/local/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' | ||||
|       success: '/path/to/session/tokenSubmitSuccess-page.html' | ||||
|       failure: '/path/to/session/tokenSubmitFailure-page.html' | ||||
| # CONFIGURATION EXAMPLE | ||||
| # DO NOT COPY/PASTE THIS IN YOUR CONFIGURATION | ||||
| ``` | ||||
|  | ||||
| 3PID session are divided into three config sections: | ||||
| - `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 | ||||
| `view.session`: | ||||
| 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 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. | ||||
|  | ||||
| ## Local & Remote | ||||
| ### 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 | ||||
| ### Failure | ||||
| No object/placeholder are currently available. | ||||
|   | ||||
| @@ -1,9 +1,8 @@ | ||||
| # 3PID Sessions | ||||
| - [Overview](#overview) | ||||
| - [Purpose](#purpose) | ||||
| - [Federation](#federation) | ||||
|   - [3PID scope](#3pid-scope) | ||||
|   - [Session scope](#session-scope) | ||||
| - [Restrictions](#restrictions) | ||||
|   - [Bindings](#bindings) | ||||
|   - [Federation](#federation) | ||||
| - [Notifications](#notifications) | ||||
|   - [Email](#email) | ||||
|   - [Phone numbers](#msisdn-(phone-numbers)) | ||||
| @@ -11,86 +10,65 @@ | ||||
|   - [Configuration](#configuration) | ||||
|   - [Web views](#web-views) | ||||
|   - [Scenarios](#scenarios) | ||||
|     - [Default](#default) | ||||
|     - [Local sessions only](#local-sessions-only) | ||||
|     - [Remote sessions only](#remote-sessions-only) | ||||
|     - [Sessions disabled](#sessions-disabled) | ||||
|  | ||||
| ## Overview | ||||
| When adding an email, a phone number or any other kind of 3PID (Third-Party Identifier) in a Matrix client, | ||||
| the identity server is called to validate the 3PID. | ||||
| the identity server is contacted to validate the 3PID. | ||||
|  | ||||
| Once this 3PID is validated, the Homeserver will publish the user Matrix ID on the Identity Server and | ||||
| add this 3PID to the Matrix account which initiated the request. | ||||
| To validate the 3PID, the identity server creates a session associated with a secret token. That token is sent via a message | ||||
| to the 3PID (e.g. an email) with a the necessary info so the user can submit them to the Identity Server, confirm ownership | ||||
| of the 3PID. | ||||
|  | ||||
| Once this 3PID is validated, the Homeserver will request that the Identity Server links the provided user Matrix ID with | ||||
| the 3PID session and finally add the 3PID to its own data store. | ||||
|  | ||||
| ## Purpose | ||||
| This serves two purposes: | ||||
| - 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. | ||||
|  | ||||
| ## Federation | ||||
| Federation is based on the principle that one can get a domain name and serve services and information within that | ||||
| domain namespace in a way which can be discovered following a specific protocol or specification. | ||||
| ## 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. | ||||
|  | ||||
| In the Matrix eco-system, some 3PID can be federated (e.g. emails) while some others cannot (phone numbers). | ||||
| Also, Matrix users might add 3PIDs that would not point to the Identity server that actually holds the 3PID binding.   | ||||
| 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. | ||||
|  | ||||
| Federation is based on the principle that each server is responsible for its own (dns) domain. | ||||
| Therefore only those 3PID can be federated that can be distinguished by their | ||||
| domain such as email addresses. | ||||
|  | ||||
| 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`. | ||||
|  | ||||
| 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). | ||||
| 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`.   | ||||
| If a federated lookup was performed, Identity servers would try to find the 3PID bind at the `gmail.com` server, and | ||||
| not `example.org`. | ||||
|  | ||||
| To allow global publishing of 3PID bindings to be found anywhere within the current protocol specification, one would | ||||
| perform 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. | ||||
|  | ||||
| On the flip side, 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 rely 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 looked up using federation and that such 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. | ||||
| As ma1sd is built for self-hosted use cases, mainly for orgs/corps, this is usually not a problem for emails.   | ||||
| Sadly, there is currently no mechanism to make this work for phone numbers.  | ||||
|  | ||||
| ## Notifications | ||||
| 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 | ||||
| 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. | ||||
| - Connector which actually send the notification (e.g. SMTP for email). | ||||
|  | ||||
| @@ -112,42 +90,32 @@ Connectors: | ||||
|  | ||||
| ## Usage | ||||
| ### 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 | ||||
| file unless you want to specifically overwrite them. | ||||
| ```yaml | ||||
| # DO NOT COPY/PASTE THIS IN YOUR CONFIGURATION | ||||
| session.policy.validation.enabled: true | ||||
| session.policy.validation.forLocal: | ||||
| # CONFIGURATION EXAMPLE | ||||
| # DO NOT COPY/PASTE AS-IS IN YOUR CONFIGURATION | ||||
|  | ||||
| session: | ||||
|   policy: | ||||
|     validation: | ||||
|       enabled: true | ||||
|   toLocal: true | ||||
|   toRemote: | ||||
|     unbind: | ||||
|       notification: | ||||
|         enabled: true | ||||
|     server: 'configExample'  # Not to be included in config! Already present in default config! | ||||
| session.policy.validation.forRemote: | ||||
|   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 | ||||
|  | ||||
| # DO NOT COPY/PASTE AS-IS IN YOUR CONFIGURATION | ||||
| # CONFIGURATION EXAMPLE | ||||
| ``` | ||||
|  | ||||
| `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 is divided contains 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: | ||||
| - 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. | ||||
| `unbind` controls warning notifications for 3PID removal.   | ||||
|  | ||||
| ### Web views | ||||
| Once a user click on a validation link, it is taken to the Identity Server validation page where the token is submitted.   | ||||
| @@ -158,103 +126,18 @@ See [the dedicated document](session-views.md) | ||||
| on how to configure/customize/brand those pages to your liking. | ||||
|  | ||||
| ### 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 | ||||
| This configuration would disable 3PID session altogether, preventing users from adding emails and/or phone numbers 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 configuration would disable 3PID sessions altogether, preventing users from validating emails and/or phone numbers | ||||
| and any subsequent actions that requires them, like adding them to their profiles. | ||||
|    | ||||
| **This mode comes with several important restrictions:** | ||||
| - This does not prevent users from removing 3PID from their profile. They would be unable to add them back! | ||||
| - This prevents users from initiating remote session to make their 3PID binds globally visible | ||||
|  | ||||
| It is therefore recommended to not fully disable sessions but instead restrict specific set of 3PID and Session scopes. | ||||
| This would be used if ma1sd is also performing authentication for the Homeserver, typically with synapse and the | ||||
| [REST password provider](https://github.com/ma1uta/matrix-synapse-rest-password-provider), where 3PID mappings would be | ||||
| auto-populated. | ||||
|  | ||||
| Use the following values to enable this mode: | ||||
| ```yaml | ||||
| session.policy.validation.enabled: false | ||||
| session: | ||||
|   policy: | ||||
|     validation: | ||||
|       enabled: false | ||||
| ``` | ||||
|   | ||||
							
								
								
									
										58
									
								
								docs/troubleshooting.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								docs/troubleshooting.md
									
									
									
									
									
										Normal 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). | ||||
							
								
								
									
										
											BIN
										
									
								
								gradle/wrapper/gradle-wrapper.jar
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								gradle/wrapper/gradle-wrapper.jar
									
									
									
									
										vendored
									
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										6
									
								
								gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
								
							| @@ -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 | ||||
| distributionPath=wrapper/dists | ||||
| zipStoreBase=GRADLE_USER_HOME | ||||
| zipStorePath=wrapper/dists | ||||
| distributionUrl=https\://services.gradle.org/distributions/gradle-4.0.2-bin.zip | ||||
| zipStoreBase=GRADLE_USER_HOME | ||||
|   | ||||
							
								
								
									
										18
									
								
								gradlew
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										18
									
								
								gradlew
									
									
									
									
										vendored
									
									
								
							| @@ -1,5 +1,21 @@ | ||||
| #!/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 | ||||
| @@ -28,7 +44,7 @@ APP_NAME="Gradle" | ||||
| APP_BASE_NAME=`basename "$0"` | ||||
|  | ||||
| # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. | ||||
| DEFAULT_JVM_OPTS="" | ||||
| DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' | ||||
|  | ||||
| # Use the maximum available, or set MAX_FD != -1 to use that value. | ||||
| MAX_FD="maximum" | ||||
|   | ||||
							
								
								
									
										18
									
								
								gradlew.bat
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										18
									
								
								gradlew.bat
									
									
									
									
										vendored
									
									
								
							| @@ -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 | ||||
| @rem ########################################################################## | ||||
| @rem | ||||
| @@ -14,7 +30,7 @@ set APP_BASE_NAME=%~n0 | ||||
| set APP_HOME=%DIRNAME% | ||||
|  | ||||
| @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. | ||||
| set DEFAULT_JVM_OPTS= | ||||
| set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" | ||||
|  | ||||
| @rem Find java.exe | ||||
| if defined JAVA_HOME goto findJavaFromJavaHome | ||||
|   | ||||
							
								
								
									
										111
									
								
								ma1sd.example.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								ma1sd.example.yaml
									
									
									
									
									
										Normal 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" | ||||
| @@ -1,7 +1,9 @@ | ||||
| Package: mxisd | ||||
| Maintainer: Kamax.io <foss@kamax.io> | ||||
| Homepage: https://github.com/kamax-io/mxisd | ||||
| Package: ma1sd | ||||
| Maintainer: ma1uta <sablintolya@gmail.com> | ||||
| Homepage: https://github.com/ma1uta/ma1sd | ||||
| Description: Federated Matrix Identity Server | ||||
| 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 | ||||
|   | ||||
| @@ -1,13 +1,19 @@ | ||||
| #!/bin/bash -e | ||||
|  | ||||
| # Add service account | ||||
| useradd -r mxisd || true | ||||
| useradd -r ma1sd || true | ||||
|  | ||||
| # Set permissions for data directory | ||||
| chown -R mxisd:mxisd %DEB_DATA_DIR% | ||||
| chown -R ma1sd:ma1sd %DEB_DATA_DIR% | ||||
|  | ||||
| # Create symlink to mxusd | ||||
| ln -sfT /usr/lib/mxisd/mxisd.jar /usr/bin/mxisd | ||||
| # Create symlink to ma1sd run script | ||||
| ln -sfT /usr/lib/ma1sd/ma1sd /usr/bin/ma1sd | ||||
|  | ||||
| # 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 | ||||
|   | ||||
| @@ -1,10 +1,10 @@ | ||||
| #!/bin/bash | ||||
|  | ||||
| # Stop running instance if needed | ||||
| systemctl stop mxisd.service | ||||
| systemctl stop ma1sd.service | ||||
|  | ||||
| # Disable service if exists | ||||
| systemctl disable mxisd.service | ||||
| systemctl disable ma1sd.service | ||||
|  | ||||
| # remove symlink | ||||
| rm /usr/bin/mxisd | ||||
| rm /usr/bin/ma1sd | ||||
|   | ||||
| @@ -1,25 +1,34 @@ | ||||
| #!/usr/bin/env bash | ||||
| #!/bin/bash | ||||
|  | ||||
| if [[ -n "$CONF_FILE_PATH" ]] && [ ! -f "$CONF_FILE_PATH" ]; then | ||||
|     echo "Generating config file $CONF_FILE_PATH" | ||||
|     touch "CONF_FILE_PATH" | ||||
|  | ||||
|     if [[ -n "$MATRIX_DOMAIN" ]]; then | ||||
|         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 | ||||
|  | ||||
|     if [[ -n "$SIGN_KEY_PATH" ]]; then | ||||
|         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 | ||||
|  | ||||
|     if [[ -n "$SQLITE_DATABASE_PATH" ]]; then | ||||
|         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 | ||||
|  | ||||
|     echo "Starting mxisd..." | ||||
|     echo "Starting ma1sd..." | ||||
|     echo | ||||
| 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 | ||||
|   | ||||
| @@ -1,27 +1,26 @@ | ||||
| /* | ||||
| * The MIT License | ||||
| * | ||||
| * Copyright (c) 2013 Edin Dazdarevic (edin.dazdarevic@gmail.com) | ||||
|  * The MIT License | ||||
|  * | ||||
|  * Copyright (c) 2013 Edin Dazdarevic (edin.dazdarevic@gmail.com) | ||||
|  | ||||
| * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| * of this software and associated documentation files (the "Software"), to deal | ||||
| * in the Software without restriction, including without limitation the rights | ||||
| * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
| * copies of the Software, and to permit persons to whom the Software is | ||||
| * furnished to do so, subject to the following conditions: | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  * of this software and associated documentation files (the "Software"), to deal | ||||
|  * in the Software without restriction, including without limitation the rights | ||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  * copies of the Software, and to permit persons to whom the Software is | ||||
|  * furnished to do so, subject to the following conditions: | ||||
|  | ||||
| * The above copyright notice and this permission notice shall be included in | ||||
| * all copies or substantial portions of the Software. | ||||
|  * The above copyright notice and this permission notice shall be included in | ||||
|  * all copies or substantial portions of the Software. | ||||
|  | ||||
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| * 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 | ||||
| * THE SOFTWARE. | ||||
| * | ||||
| * */ | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * 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 | ||||
|  * THE SOFTWARE. | ||||
|  */ | ||||
|  | ||||
| package edazdarevic.commons.net; | ||||
|  | ||||
| @@ -37,6 +36,7 @@ import java.util.List; | ||||
|  * both IPv4 and IPv6. | ||||
|  */ | ||||
| public class CIDRUtils { | ||||
|  | ||||
|     private final String cidr; | ||||
|  | ||||
|     private InetAddress inetAddress; | ||||
| @@ -44,7 +44,6 @@ public class CIDRUtils { | ||||
|     private InetAddress endAddress; | ||||
|     private final int prefixLength; | ||||
|  | ||||
|  | ||||
|     public CIDRUtils(String cidr) throws UnknownHostException { | ||||
|  | ||||
|         this.cidr = cidr; | ||||
| @@ -66,7 +65,6 @@ public class CIDRUtils { | ||||
|  | ||||
|  | ||||
|     private void calculate() throws UnknownHostException { | ||||
|  | ||||
|         ByteBuffer maskBuffer; | ||||
|         int targetSize; | ||||
|         if (inetAddress.getAddress().length == 4) { | ||||
| @@ -120,14 +118,9 @@ public class CIDRUtils { | ||||
|     } | ||||
|  | ||||
|     public String getNetworkAddress() { | ||||
|  | ||||
|         return this.startAddress.getHostAddress(); | ||||
|     } | ||||
|  | ||||
|     public String getBroadcastAddress() { | ||||
|         return this.endAddress.getHostAddress(); | ||||
|     } | ||||
|  | ||||
|     public boolean isInRange(String ipAddress) throws UnknownHostException { | ||||
|         InetAddress address = InetAddress.getByName(ipAddress); | ||||
|         BigInteger start = new BigInteger(1, this.startAddress.getAddress()); | ||||
| @@ -139,4 +132,5 @@ public class CIDRUtils { | ||||
|  | ||||
|         return (st == -1 || st == 0) && (te == -1 || te == 0); | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
							
								
								
									
										33
									
								
								src/main/java/io/kamax/matrix/MalformedEventException.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/main/java/io/kamax/matrix/MalformedEventException.java
									
									
									
									
									
										Normal 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"); | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										44
									
								
								src/main/java/io/kamax/matrix/MatrixErrorInfo.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								src/main/java/io/kamax/matrix/MatrixErrorInfo.java
									
									
									
									
									
										Normal 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; | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										48
									
								
								src/main/java/io/kamax/matrix/MatrixException.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								src/main/java/io/kamax/matrix/MatrixException.java
									
									
									
									
									
										Normal 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; | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										170
									
								
								src/main/java/io/kamax/matrix/MatrixID.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										170
									
								
								src/main/java/io/kamax/matrix/MatrixID.java
									
									
									
									
									
										Normal 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(); | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										93
									
								
								src/main/java/io/kamax/matrix/MatrixIdCodec.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								src/main/java/io/kamax/matrix/MatrixIdCodec.java
									
									
									
									
									
										Normal 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(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										104
									
								
								src/main/java/io/kamax/matrix/MatrixPath.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								src/main/java/io/kamax/matrix/MatrixPath.java
									
									
									
									
									
										Normal 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()); | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										61
									
								
								src/main/java/io/kamax/matrix/ThreePid.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								src/main/java/io/kamax/matrix/ThreePid.java
									
									
									
									
									
										Normal 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; | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										61
									
								
								src/main/java/io/kamax/matrix/ThreePidMapping.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								src/main/java/io/kamax/matrix/ThreePidMapping.java
									
									
									
									
									
										Normal 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; | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										46
									
								
								src/main/java/io/kamax/matrix/ThreePidMedium.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								src/main/java/io/kamax/matrix/ThreePidMedium.java
									
									
									
									
									
										Normal 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); | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										41
									
								
								src/main/java/io/kamax/matrix/_MatrixContent.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								src/main/java/io/kamax/matrix/_MatrixContent.java
									
									
									
									
									
										Normal 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(); | ||||
|  | ||||
| } | ||||
							
								
								
									
										55
									
								
								src/main/java/io/kamax/matrix/_MatrixID.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								src/main/java/io/kamax/matrix/_MatrixID.java
									
									
									
									
									
										Normal 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(); | ||||
|  | ||||
| } | ||||
							
								
								
									
										31
									
								
								src/main/java/io/kamax/matrix/_MatrixUser.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/main/java/io/kamax/matrix/_MatrixUser.java
									
									
									
									
									
										Normal 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(); | ||||
|  | ||||
| } | ||||
							
								
								
									
										42
									
								
								src/main/java/io/kamax/matrix/_MatrixUserProfile.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/main/java/io/kamax/matrix/_MatrixUserProfile.java
									
									
									
									
									
										Normal 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(); | ||||
|  | ||||
| } | ||||
							
								
								
									
										29
									
								
								src/main/java/io/kamax/matrix/_ThreePid.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								src/main/java/io/kamax/matrix/_ThreePid.java
									
									
									
									
									
										Normal 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(); | ||||
|  | ||||
| } | ||||
							
								
								
									
										29
									
								
								src/main/java/io/kamax/matrix/_ThreePidMapping.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								src/main/java/io/kamax/matrix/_ThreePidMapping.java
									
									
									
									
									
										Normal 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(); | ||||
|  | ||||
| } | ||||
							
								
								
									
										409
									
								
								src/main/java/io/kamax/matrix/client/AMatrixHttpClient.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										409
									
								
								src/main/java/io/kamax/matrix/client/AMatrixHttpClient.java
									
									
									
									
									
										Normal 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(); | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										154
									
								
								src/main/java/io/kamax/matrix/client/MatrixClientContext.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										154
									
								
								src/main/java/io/kamax/matrix/client/MatrixClientContext.java
									
									
									
									
									
										Normal 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; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -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; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -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); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -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); | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										133
									
								
								src/main/java/io/kamax/matrix/client/MatrixHttpContent.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										133
									
								
								src/main/java/io/kamax/matrix/client/MatrixHttpContent.java
									
									
									
									
									
										Normal 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(); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -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; | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										93
									
								
								src/main/java/io/kamax/matrix/client/MatrixHttpPushRule.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								src/main/java/io/kamax/matrix/client/MatrixHttpPushRule.java
									
									
									
									
									
										Normal 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))))); | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										50
									
								
								src/main/java/io/kamax/matrix/client/MatrixHttpRequest.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								src/main/java/io/kamax/matrix/client/MatrixHttpRequest.java
									
									
									
									
									
										Normal 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; | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										440
									
								
								src/main/java/io/kamax/matrix/client/MatrixHttpRoom.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										440
									
								
								src/main/java/io/kamax/matrix/client/MatrixHttpRoom.java
									
									
									
									
									
										Normal 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()); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										125
									
								
								src/main/java/io/kamax/matrix/client/MatrixHttpUser.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										125
									
								
								src/main/java/io/kamax/matrix/client/MatrixHttpUser.java
									
									
									
									
									
										Normal 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))); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -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; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										45
									
								
								src/main/java/io/kamax/matrix/client/PresenceStatus.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								src/main/java/io/kamax/matrix/client/PresenceStatus.java
									
									
									
									
									
										Normal 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); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -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); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -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(); | ||||
|  | ||||
| } | ||||
| @@ -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(); | ||||
|  | ||||
| } | ||||
							
								
								
									
										158
									
								
								src/main/java/io/kamax/matrix/client/_MatrixClient.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										158
									
								
								src/main/java/io/kamax/matrix/client/_MatrixClient.java
									
									
									
									
									
										Normal 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); | ||||
|  | ||||
| } | ||||
							
								
								
									
										51
									
								
								src/main/java/io/kamax/matrix/client/_MatrixClientRaw.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								src/main/java/io/kamax/matrix/client/_MatrixClientRaw.java
									
									
									
									
									
										Normal 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(); | ||||
|  | ||||
| } | ||||
							
								
								
									
										29
									
								
								src/main/java/io/kamax/matrix/client/_Presence.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								src/main/java/io/kamax/matrix/client/_Presence.java
									
									
									
									
									
										Normal 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(); | ||||
|  | ||||
| } | ||||
							
								
								
									
										43
									
								
								src/main/java/io/kamax/matrix/client/_PushRule.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								src/main/java/io/kamax/matrix/client/_PushRule.java
									
									
									
									
									
										Normal 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); | ||||
|  | ||||
| } | ||||
							
								
								
									
										253
									
								
								src/main/java/io/kamax/matrix/client/_SyncData.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										253
									
								
								src/main/java/io/kamax/matrix/client/_SyncData.java
									
									
									
									
									
										Normal 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(); | ||||
|  | ||||
| } | ||||
							
								
								
									
										65
									
								
								src/main/java/io/kamax/matrix/client/_SyncOptions.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								src/main/java/io/kamax/matrix/client/_SyncOptions.java
									
									
									
									
									
										Normal 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(); | ||||
|  | ||||
| } | ||||
| @@ -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); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -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); | ||||
|  | ||||
| } | ||||
| @@ -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"); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -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); | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										48
									
								
								src/main/java/io/kamax/matrix/client/regular/Presence.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								src/main/java/io/kamax/matrix/client/regular/Presence.java
									
									
									
									
									
										Normal 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; | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										390
									
								
								src/main/java/io/kamax/matrix/client/regular/SyncDataJson.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										390
									
								
								src/main/java/io/kamax/matrix/client/regular/SyncDataJson.java
									
									
									
									
									
										Normal 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; | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										103
									
								
								src/main/java/io/kamax/matrix/client/regular/SyncOptions.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								src/main/java/io/kamax/matrix/client/regular/SyncOptions.java
									
									
									
									
									
										Normal 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); | ||||
|     } | ||||
|  | ||||
| } | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user