Compare commits
	
		
			49 Commits
		
	
	
		
			v1.2.1
			...
			v1.3.0-rc.
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 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 | 
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -7,6 +7,7 @@ out/ | |||||||
| .idea/ | .idea/ | ||||||
|  |  | ||||||
| # Local dev config | # Local dev config | ||||||
|  | /mxisd.yaml | ||||||
| /application.yaml | /application.yaml | ||||||
|  |  | ||||||
| # Local dev storage | # Local dev storage | ||||||
|   | |||||||
| @@ -14,4 +14,5 @@ ENV SQLITE_DATABASE_PATH="/var/mxisd/mxisd.db" | |||||||
| CMD [ "/start.sh" ] | CMD [ "/start.sh" ] | ||||||
|  |  | ||||||
| ADD src/docker/start.sh /start.sh | ADD src/docker/start.sh /start.sh | ||||||
| ADD build/libs/mxisd.jar /mxisd.jar | ADD src/script/mxisd /app/mxisd | ||||||
|  | ADD build/libs/mxisd.jar /app/mxisd.jar | ||||||
|   | |||||||
							
								
								
									
										21
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								README.md
									
									
									
									
									
								
							| @@ -8,18 +8,20 @@ mxisd - Federated Matrix Identity Server | |||||||
| - [Getting Started](#getting-started) | - [Getting Started](#getting-started) | ||||||
| - [Support](#support) | - [Support](#support) | ||||||
| - [Contribute](#contribute) | - [Contribute](#contribute) | ||||||
|  | - [Powered by mxisd](#powered-by-mxisd) | ||||||
| - [FAQ](#faq) | - [FAQ](#faq) | ||||||
| - [Contact](#contact) | - [Contact](#contact) | ||||||
|  |  | ||||||
| # Overview | # Overview | ||||||
| mxisd is a Federated Matrix Identity server for self-hosted Matrix infrastructures with [enhanced features](#features). | 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) | As an enhanced Identity service, it implements the [Identity service API](https://matrix.org/docs/spec/identity_service/r0.1.0.html) | ||||||
| and several [extra features](#features) that greatly enhance user experience within Matrix. | and several [extra features](#features) that greatly enhance user experience within Matrix. | ||||||
| It is the one stop shop for anything regarding Authentication, Directory and Identity management in Matrix built in a | It is the one stop shop for anything regarding Authentication, Directory and Identity management in Matrix built in a | ||||||
| single coherent product. | single coherent product. | ||||||
|    |    | ||||||
| mxisd is specifically designed to connect to an existing on-premise Identity store (AD/Samba/LDAP, SQL Database, | mxisd is specifically designed to connect to an existing on-premise Identity store (AD/Samba/LDAP, SQL Database, | ||||||
| Web services/app, etc.) and ease the integration of a Matrix infrastructure within an existing one.   | Web services/app, etc.) and ease the integration of a Matrix infrastructure within an existing one.   | ||||||
|  | Check [our FAQ entry](docs/faq.md#what-kind-of-setup-is-mxisd-really-designed-for) to know if mxisd 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 mxisd is to map between Matrix IDs and 3PIDs (Third-Party IDentifiers) for the Homeserver and its | ||||||
| users. 3PIDs can be anything that uniquely and globally identify a user, like: | users. 3PIDs can be anything that uniquely and globally identify a user, like: | ||||||
| @@ -32,15 +34,15 @@ users. 3PIDs can be anything that uniquely and globally identify a user, like: | |||||||
| If you are unfamiliar with the Identity vocabulary and concepts in Matrix, **please read this [introduction](docs/concepts.md)**. | If you are unfamiliar with the Identity vocabulary and concepts in Matrix, **please read this [introduction](docs/concepts.md)**. | ||||||
|  |  | ||||||
| # Features | # Features | ||||||
| [Identity](docs/features/identity.md): As a [regular Matrix Identity service](https://kamax.io/matrix/api/identity_service/unstable.html#general-principles): | [Identity](docs/features/identity.md): As a [regular Matrix Identity service](https://matrix.org/docs/spec/identity_service/r0.1.0.html#general-principles): | ||||||
| - Search for people by 3PID using its own Identity stores | - Search for people by 3PID using its own Identity stores | ||||||
|   ([Spec](https://kamax.io/matrix/api/identity_service/unstable.html#association-lookup)) |   ([Spec](https://matrix.org/docs/spec/identity_service/r0.1.0.html#association-lookup)) | ||||||
| - Invite people to rooms by 3PID using its own Identity stores, with notifications to the invitee (Email, SMS, etc.) | - Invite people to rooms by 3PID using its own Identity stores, with notifications to the invitee (Email, SMS, etc.) | ||||||
|   ([Spec](https://kamax.io/matrix/api/identity_service/unstable.html#post-matrix-identity-api-v1-store-invite)) |   ([Spec](https://matrix.org/docs/spec/identity_service/r0.1.0.html#post-matrix-identity-api-v1-store-invite)) | ||||||
| - Allow users to add 3PIDs to their settings/profile | - 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.1.0.html#establishing-associations)) | ||||||
| - Register accounts on your Homeserver with 3PIDs | - Register accounts on your Homeserver with 3PIDs | ||||||
|   ([Spec](https://kamax.io/matrix/api/identity_service/unstable.html#establishing-associations)) |   ([Spec](https://matrix.org/docs/spec/identity_service/r0.1.0.html#establishing-associations)) | ||||||
|  |  | ||||||
| As an enhanced Identity service: | As an enhanced Identity service: | ||||||
| - [Federation](docs/features/federation.md): Use a recursive lookup mechanism when searching and inviting people by 3PID, | - [Federation](docs/features/federation.md): Use a recursive lookup mechanism when searching and inviting people by 3PID, | ||||||
| @@ -66,6 +68,8 @@ As an enhanced Identity service: | |||||||
| - Users can directly find each other using whatever attribute is relevant within your Identity store | - Users can directly find each other using whatever attribute is relevant within your Identity store | ||||||
| - Federate your Identity server so you can discover others and/or others can discover you | - Federate your Identity server so you can discover others and/or others can discover you | ||||||
|  |  | ||||||
|  | Also, check [our FAQ entry](docs/faq.md#what-kind-of-setup-is-mxisd-really-designed-for) to know if mxisd is a good fit for you. | ||||||
|  |  | ||||||
| # Getting started | # Getting started | ||||||
| See the [dedicated document](docs/getting-started.md) | See the [dedicated document](docs/getting-started.md) | ||||||
|  |  | ||||||
| @@ -96,6 +100,11 @@ You can contribute as an organisation/corporation by: | |||||||
| maintained regularly and you get direct access to the support team. | 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. | - Sponsoring new features or bug fixes. [Get in touch](#contact) so we can discuss it further. | ||||||
|  |  | ||||||
|  | # Powered by mxisd | ||||||
|  | The following projects use mxisd 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 | # FAQ | ||||||
| See the [dedicated document](docs/faq.md) | See the [dedicated document](docs/faq.md) | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										98
									
								
								build.gradle
									
									
									
									
									
								
							
							
						
						
									
										98
									
								
								build.gradle
									
									
									
									
									
								
							| @@ -21,9 +21,11 @@ | |||||||
| import java.util.regex.Pattern | import java.util.regex.Pattern | ||||||
|  |  | ||||||
| apply plugin: 'java' | apply plugin: 'java' | ||||||
| apply plugin: 'org.springframework.boot' | apply plugin: 'application' | ||||||
|  | apply plugin: 'com.github.johnrengelman.shadow' | ||||||
|  | apply plugin: 'idea' | ||||||
|  |  | ||||||
| def confFileName = "application.example.yaml" | def confFileName = "mxisd.example.yaml" | ||||||
| def distDir = "${project.buildDir}/dist" | def distDir = "${project.buildDir}/dist" | ||||||
|  |  | ||||||
| def debBinPath = "/usr/lib/mxisd" | def debBinPath = "/usr/lib/mxisd" | ||||||
| @@ -31,7 +33,8 @@ def debConfPath = "/etc/mxisd" | |||||||
| def debDataPath = "/var/lib/mxisd" | def debDataPath = "/var/lib/mxisd" | ||||||
| def debSystemdPath = "/etc/systemd/system" | def debSystemdPath = "/etc/systemd/system" | ||||||
|  |  | ||||||
| def debConfFileName = "mxisd-sample.yaml" | def debConfFileName = confFileName | ||||||
|  | def debStartScriptFilename = "mxisd" | ||||||
|  |  | ||||||
| def debBuildBasePath = "${project.buildDir}/tmp/debian" | def debBuildBasePath = "${project.buildDir}/tmp/debian" | ||||||
| def debBuildDebianPath = "${debBuildBasePath}/DEBIAN" | def debBuildDebianPath = "${debBuildBasePath}/DEBIAN" | ||||||
| @@ -43,6 +46,9 @@ def debBuildSystemdPath = "${debBuildBasePath}${debSystemdPath}" | |||||||
| def dockerImageName = "kamax/mxisd" | def dockerImageName = "kamax/mxisd" | ||||||
| def dockerImageTag = "${dockerImageName}:${mxisdVersion()}" | def dockerImageTag = "${dockerImageName}:${mxisdVersion()}" | ||||||
|  |  | ||||||
|  | group = 'io.kamax' | ||||||
|  | mainClassName = 'io.kamax.mxisd.MxisdStandaloneExec' | ||||||
|  |  | ||||||
| String mxisdVersion() { | String mxisdVersion() { | ||||||
|     def versionPattern = Pattern.compile("v(\\d+\\.)?(\\d+\\.)?(\\d+)(-.*)?") |     def versionPattern = Pattern.compile("v(\\d+\\.)?(\\d+\\.)?(\\d+)(-.*)?") | ||||||
|  |  | ||||||
| @@ -59,38 +65,41 @@ String gitVersion() { | |||||||
|         commandLine = ['git', 'describe', '--tags', '--always', '--dirty'] |         commandLine = ['git', 'describe', '--tags', '--always', '--dirty'] | ||||||
|         standardOutput = out |         standardOutput = out | ||||||
|     } |     } | ||||||
|     return out.toString().replace(System.lineSeparator(), ''); |     return out.toString().replace(System.lineSeparator(), '') | ||||||
| } | } | ||||||
|  |  | ||||||
| buildscript { | buildscript { | ||||||
|     repositories { |     repositories { | ||||||
|         mavenCentral() |         jcenter() | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     dependencies { |     dependencies { | ||||||
|         classpath 'org.springframework.boot:spring-boot-gradle-plugin:1.5.3.RELEASE' |         classpath 'com.github.jengelman.gradle.plugins:shadow:4.0.3' | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| repositories { | repositories { | ||||||
|     mavenCentral() |     jcenter() | ||||||
|     maven { url "https://kamax.io/maven/releases/" } |     maven { url "https://kamax.io/maven/releases/" } | ||||||
|     maven { url "https://kamax.io/maven/snapshots/" } |     maven { url "https://kamax.io/maven/snapshots/" } | ||||||
| } | } | ||||||
|  |  | ||||||
| dependencies { | dependencies { | ||||||
|  |     // Logging | ||||||
|  |     compile 'org.slf4j:slf4j-simple:1.7.25' | ||||||
|  |      | ||||||
|     // Easy file management |     // Easy file management | ||||||
|     compile 'commons-io:commons-io:2.5' |     compile 'commons-io:commons-io:2.5' | ||||||
|      |      | ||||||
|     // Spring Boot - standalone app |     // Config management | ||||||
|     compile 'org.springframework.boot:spring-boot-starter-web:1.5.10.RELEASE' |     compile 'org.yaml:snakeyaml:1.23' | ||||||
|  |  | ||||||
|     // Thymeleaf for HTML templates |  | ||||||
|     compile "org.springframework.boot:spring-boot-starter-thymeleaf:1.5.10.RELEASE" |  | ||||||
|  |  | ||||||
|     // Matrix Java SDK |     // Matrix Java SDK | ||||||
|     compile 'io.kamax:matrix-java-sdk:0.0.14-8-g0e57ec6' |     compile 'io.kamax:matrix-java-sdk:0.0.14-8-g0e57ec6' | ||||||
|  |  | ||||||
|  |     // ORMLite | ||||||
|  |     compile 'com.j256.ormlite:ormlite-jdbc:5.0' | ||||||
|  |  | ||||||
|     // ed25519 handling |     // ed25519 handling | ||||||
|     compile 'net.i2p.crypto:eddsa:0.1.0' |     compile 'net.i2p.crypto:eddsa:0.1.0' | ||||||
|  |  | ||||||
| @@ -107,15 +116,12 @@ dependencies { | |||||||
|     compile 'com.googlecode.libphonenumber:libphonenumber:8.7.1' |     compile 'com.googlecode.libphonenumber:libphonenumber:8.7.1' | ||||||
|  |  | ||||||
|     // E-mail sending |     // E-mail sending | ||||||
|     compile 'com.sun.mail:javax.mail:1.6.2' |  | ||||||
|     compile 'javax.mail:javax.mail-api:1.6.2' |     compile 'javax.mail:javax.mail-api:1.6.2' | ||||||
|  |     compile 'com.sun.mail:javax.mail:1.6.2' | ||||||
|  |  | ||||||
|     // Google Firebase Authentication backend |     // Google Firebase Authentication backend | ||||||
|     compile 'com.google.firebase:firebase-admin:5.3.0' |     compile 'com.google.firebase:firebase-admin:5.3.0' | ||||||
|  |  | ||||||
|     // ORMLite |  | ||||||
|     compile 'com.j256.ormlite:ormlite-jdbc:5.0' |  | ||||||
|  |  | ||||||
|     // Connection Pool |     // Connection Pool | ||||||
|     compile 'com.mchange:c3p0:0.9.5.2' |     compile 'com.mchange:c3p0:0.9.5.2' | ||||||
|  |  | ||||||
| @@ -123,7 +129,7 @@ dependencies { | |||||||
|     compile 'org.xerial:sqlite-jdbc:3.20.0' |     compile 'org.xerial:sqlite-jdbc:3.20.0' | ||||||
|  |  | ||||||
|     // PostgreSQL |     // PostgreSQL | ||||||
|     compile 'org.postgresql:postgresql:42.1.4' |     compile 'org.postgresql:postgresql:42.2.5' | ||||||
|  |  | ||||||
|     // MariaDB/MySQL |     // MariaDB/MySQL | ||||||
|     compile 'org.mariadb.jdbc:mariadb-java-client:2.1.2' |     compile 'org.mariadb.jdbc:mariadb-java-client:2.1.2' | ||||||
| @@ -137,36 +143,28 @@ dependencies { | |||||||
|     // ZT-Exec for exec identity store |     // ZT-Exec for exec identity store | ||||||
|     compile 'org.zeroturnaround:zt-exec:1.10' |     compile 'org.zeroturnaround:zt-exec:1.10' | ||||||
|  |  | ||||||
|  |     // HTTP server | ||||||
|  |     compile 'io.undertow:undertow-core:2.0.16.Final' | ||||||
|  |  | ||||||
|     testCompile 'junit:junit:4.12' |     testCompile 'junit:junit:4.12' | ||||||
|     testCompile 'com.github.tomakehurst:wiremock:2.8.0' |     testCompile 'com.github.tomakehurst:wiremock:2.8.0' | ||||||
|  |     testCompile 'com.unboundid:unboundid-ldapsdk:4.0.9' | ||||||
|  |     testCompile 'com.icegreen:greenmail:1.5.9' | ||||||
| } | } | ||||||
|  |  | ||||||
| springBoot { | shadowJar { | ||||||
|     executable = true |     baseName = project.name | ||||||
|  |     classifier = null | ||||||
|     embeddedLaunchScriptProperties = [ |     version = null | ||||||
|             confFolder: "/etc/default" |  | ||||||
|     ] |  | ||||||
| } | } | ||||||
|  |  | ||||||
| processResources { | task debBuild(dependsOn: shadowJar) { | ||||||
|     doLast { |     doLast { | ||||||
|         copy { |         String debVersion = mxisdVersion() | ||||||
|             from('build/resources/main/application.yaml') { |         println "Version for package: ${debVersion}" | ||||||
|                 rename 'application.yaml', 'mxisd.yaml' |  | ||||||
|             } |  | ||||||
|             into 'build/resources/main' |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| task buildDeb(dependsOn: build) { |  | ||||||
|     doLast { |  | ||||||
|         def v = mxisdVersion() |  | ||||||
|         println "Version for package: ${v}" |  | ||||||
|         mkdir distDir |         mkdir distDir | ||||||
|         mkdir debBuildBasePath |         mkdir debBuildBasePath | ||||||
|         mkdir "${debBuildBasePath}/DEBIAN" |         mkdir debBuildDebianPath | ||||||
|         mkdir debBuildBinPath |         mkdir debBuildBinPath | ||||||
|         mkdir debBuildConfPath |         mkdir debBuildConfPath | ||||||
|         mkdir debBuildDataPath |         mkdir debBuildDataPath | ||||||
| @@ -177,10 +175,10 @@ task buildDeb(dependsOn: build) { | |||||||
|             into debBuildBinPath |             into debBuildBinPath | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         ant.chmod( |         copy { | ||||||
|                 file: "${debBuildBinPath}/mxisd.jar", |             from "${project.file("src/script/" + debStartScriptFilename)}" | ||||||
|                 perm: 'a+x' |             into debBuildBinPath | ||||||
|         ) |         } | ||||||
|  |  | ||||||
|         copy { |         copy { | ||||||
|             from(project.file(confFileName)) { |             from(project.file(confFileName)) { | ||||||
| @@ -189,16 +187,16 @@ task buildDeb(dependsOn: build) { | |||||||
|             into debBuildConfPath |             into debBuildConfPath | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         ant.replaceregexp( |         ant.replaceregexp( // FIXME adapt to new config format | ||||||
|                 file: "${debBuildConfPath}/${debConfFileName}", |                 file: "${debBuildConfPath}/${debConfFileName}", | ||||||
|                 match: "key.path:(.*)", |                 match: "key:\\R  path:(.*)", | ||||||
|                 replace: "key.path: '${debDataPath}/signing.key'" |                 replace: "key:\n  path: '${debDataPath}/signing.key'" | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|         ant.replaceregexp( |         ant.replaceregexp( // FIXME adapt to new config format | ||||||
|                 file: "${debBuildConfPath}/${debConfFileName}", |                 file: "${debBuildConfPath}/${debConfFileName}", | ||||||
|                 match: "storage.provider.sqlite.database:(.*)", |                 match: "storage:\\R  provider:\\R    sqlite:\\R      database:(.*)", | ||||||
|                 replace: "storage.provider.sqlite.database: '${debDataPath}/mxisd.db'" |                 replace: "storage:\n  provider:\n    sqlite:\n      database: '${debDataPath}/mxisd.db'" | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|         copy { |         copy { | ||||||
| @@ -209,7 +207,7 @@ task buildDeb(dependsOn: build) { | |||||||
|         ant.replace( |         ant.replace( | ||||||
|                 file: "${debBuildDebianPath}/control", |                 file: "${debBuildDebianPath}/control", | ||||||
|                 token: 'Version: 0', |                 token: 'Version: 0', | ||||||
|                 value: "Version: ${v}" |                 value: "Version: ${debVersion}" | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|         ant.replace( |         ant.replace( | ||||||
| @@ -245,7 +243,7 @@ task buildDeb(dependsOn: build) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| task dockerBuild(type: Exec, dependsOn: build) { | task dockerBuild(type: Exec, dependsOn: shadowJar) { | ||||||
|     commandLine 'docker', 'build', '-t', dockerImageTag, project.rootDir |     commandLine 'docker', 'build', '-t', dockerImageTag, project.rootDir | ||||||
|  |  | ||||||
|     doLast { |     doLast { | ||||||
|   | |||||||
| @@ -5,6 +5,7 @@ | |||||||
| - Installation | - Installation | ||||||
|   - [Debian package](install/debian.md) |   - [Debian package](install/debian.md) | ||||||
|   - [ArchLinux](install/archlinux.md) |   - [ArchLinux](install/archlinux.md) | ||||||
|  |   - [NixOS](install/nixos.md) | ||||||
|   - [Docker](install/docker.md) |   - [Docker](install/docker.md) | ||||||
|   - [From source](install/source.md) |   - [From source](install/source.md) | ||||||
| - [Architecture overview](architecture.md) | - [Architecture overview](architecture.md) | ||||||
|   | |||||||
| @@ -1 +1 @@ | |||||||
| theme: jekyll-theme-cayman | theme: jekyll-theme-hacker | ||||||
| @@ -17,9 +17,8 @@ cd mxisd | |||||||
| ./gradlew build | ./gradlew build | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| Create a new configuration file by coping `application.example.yaml` to `application.yaml` and edit to your needs.   | Create a new configuration file by coping `mxisd.example.yaml` to `mxisd.yaml` and edit to your needs.   | ||||||
| For advanced configuration, see the [Configure section](configure.md). | For advanced configuration, see the [Configure section](configure.md). | ||||||
| **NOTE**: `application.yaml` is also called `mxisd.yaml` in some specific installations. |  | ||||||
|  |  | ||||||
| Start the server in foreground to validate the build and configuration: | Start the server in foreground to validate the build and configuration: | ||||||
| ```bash | ```bash | ||||||
| @@ -29,12 +28,14 @@ java -jar build/libs/mxisd.jar | |||||||
| Ensure the signing key is available: | Ensure the signing key is available: | ||||||
| ```bash | ```bash | ||||||
| $ curl 'http://localhost:8090/_matrix/identity/api/v1/pubkey/ed25519:0' | $ curl 'http://localhost:8090/_matrix/identity/api/v1/pubkey/ed25519:0' | ||||||
|  |  | ||||||
| {"public_key":"..."} | {"public_key":"..."} | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| Test basic recursive lookup (requires Internet connection with access to TCP 443): | Test basic recursive lookup (requires Internet connection with access to TCP 443): | ||||||
| ```bash | ```bash | ||||||
| $ curl 'http://localhost:8090/_matrix/identity/api/v1/lookup?medium=email&address=mxisd-federation-test@kamax.io' | $ curl 'http://localhost:8090/_matrix/identity/api/v1/lookup?medium=email&address=mxisd-federation-test@kamax.io' | ||||||
|  |  | ||||||
| {"address":"mxisd-federation-test@kamax.io","medium":"email","mxid":"@mxisd-lookup-test:kamax.io",...} | {"address":"mxisd-federation-test@kamax.io","medium":"email","mxid":"@mxisd-lookup-test:kamax.io",...} | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| @@ -57,7 +58,7 @@ Requirements: | |||||||
|  |  | ||||||
| [Build mxisd](#build) then: | [Build mxisd](#build) then: | ||||||
| ```bash | ```bash | ||||||
| ./gradlew buildDeb  | ./gradlew debBuild | ||||||
| ``` | ``` | ||||||
| You will find the debian package in `build/dist`.   | You will find the debian package in `build/dist`.   | ||||||
| Then follow the instruction in the [Debian package](install/debian.md) document. | Then follow the instruction in the [Debian package](install/debian.md) document. | ||||||
|   | |||||||
| @@ -1,7 +1,6 @@ | |||||||
| # Configuration | # Configuration | ||||||
| - [Concepts](#concepts) | - [Concepts](#concepts) | ||||||
|   - [Syntax](#syntax) |   - [Syntax](#syntax) | ||||||
|   - [Variables](#variables) |  | ||||||
| - [Matrix](#matrix) | - [Matrix](#matrix) | ||||||
| - [Server](#server) | - [Server](#server) | ||||||
| - [Storage](#storage) | - [Storage](#storage) | ||||||
| @@ -11,36 +10,15 @@ | |||||||
|  |  | ||||||
| ## Concepts | ## Concepts | ||||||
| ### Syntax | ### Syntax | ||||||
| The configuration file is [YAML](http://yaml.org/) based, allowing two types of syntax. | The configuration file is [YAML](http://yaml.org/) based: | ||||||
|  |  | ||||||
| Properties-like: |  | ||||||
| ```yaml |  | ||||||
| my.config.item: 'value' |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| Object-like: |  | ||||||
| ```yaml | ```yaml | ||||||
| my: | my: | ||||||
|   config: |   config: | ||||||
|     item: 'value' |     item: 'value' | ||||||
|  |  | ||||||
| ``` | ``` | ||||||
| These can also be combined within the same file.   |  | ||||||
| Both syntax will be used interchangeably in these documents. |  | ||||||
|  |  | ||||||
| ### Variables |  | ||||||
| It is possible to copy the value of a configuration item into another using the syntax `${config.key.item}`.   |  | ||||||
| Example that will copy the value of `matrix.domain` into `server.name`: |  | ||||||
| ```yaml |  | ||||||
| matrix: |  | ||||||
|   domain: 'example.org' |  | ||||||
|  |  | ||||||
| server: |  | ||||||
|   name: '${matrix.domain}' |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| **WARNING:** mxisd might overwrite/adapt some values during launch. Those changes will not be reflected into copied keys. |  | ||||||
|  |  | ||||||
|  | When referencing keys in all documents, a property-like shorthand will be used. The shorthand for the above example would be `my.config.item` | ||||||
|  |  | ||||||
| ## Matrix | ## Matrix | ||||||
| `matrix.domain` | `matrix.domain` | ||||||
| @@ -53,17 +31,19 @@ Namespace to create arbitrary list of Identity servers, usable in other parts of | |||||||
|  |  | ||||||
| Example: | Example: | ||||||
| ```yaml | ```yaml | ||||||
| matrix.identity.servers: | matrix: | ||||||
|   myOtherServers: |   identity: | ||||||
|     - 'https://other1.example.org' |     servers: | ||||||
|     - 'https://other2.example.org' |       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 | ||||||
| - `server.name`: Public hostname of mxisd, if different from the Matrix domain. | - `server.name`: Public hostname of mxisd, if different from the Matrix domain. | ||||||
| - `server.port`: HTTP port to listen on (unencrypted) | - `server.port`: HTTP port to listen on (unencrypted) | ||||||
| - `server.publicUrl`: Defaults to `https://${server.name}` | - `server.publicUrl`: Defaults to `https://{server.name}` | ||||||
|  |  | ||||||
| ## Storage | ## Storage | ||||||
| ### SQLite | ### SQLite | ||||||
| @@ -82,19 +62,23 @@ See the dedicated documents: | |||||||
|  |  | ||||||
| Example: | Example: | ||||||
| ```yaml | ```yaml | ||||||
| notification.handler.email: 'sendgrid' | notification: | ||||||
| notification.handler.msisdn: 'raw' |   handler: | ||||||
|  |     email: 'sendgrid' | ||||||
|  |     msisdn: 'raw' | ||||||
| ``` | ``` | ||||||
| - Emails notifications would use the `sendgrid` handler, which define its own configuration under `notification.handlers.sendgrid` | - Emails notifications would use the `sendgrid` handler, which define its own configuration under `notification.handlers.sendgrid` | ||||||
| - Phone notification would use the `raw` handler, basic default built-in handler of mxisd | - Phone notification would use the `raw` handler, basic default built-in handler in mxisd | ||||||
|  |  | ||||||
| ### Handlers | ### Handlers | ||||||
| - `notification.handers.<handler ID>`: Handler-specific configuration for the given handler ID. Repeatable. | - `notification.handers.<handler ID>`: Handler-specific configuration for the given handler ID. Repeatable. | ||||||
|  |  | ||||||
| Example: | Example: | ||||||
| ```yaml | ```yaml | ||||||
| notification.handlers.raw: ... | notification: | ||||||
| notification.handlers.sendgrid: ... |   handlers: | ||||||
|  |     raw: ... | ||||||
|  |     sendgrid: ... | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| Built-in: | Built-in: | ||||||
|   | |||||||
							
								
								
									
										22
									
								
								docs/faq.md
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								docs/faq.md
									
									
									
									
									
								
							| @@ -16,6 +16,18 @@ 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/#/#mxisd:kamax.io) and we'll do our best to get you | ||||||
| started and answer questions you might have. | started and answer questions you might have. | ||||||
|  |  | ||||||
|  | ### What kind of setup is mxisd really designed for? | ||||||
|  | mxisd is primarily designed for setups that: | ||||||
|  | - [Care for their privacy](https://github.com/kamax-matrix/mxisd/wiki/mxisd-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 mxisd for.  | ||||||
|  |  | ||||||
|  | If you meet some of the conditions, but not all, mxisd will still be a good fit for you but you won't fully enjoy all its | ||||||
|  | features. | ||||||
|  |  | ||||||
| ### Do I need to use mxisd if I run a Homeserver? | ### Do I need to use mxisd if I run a Homeserver? | ||||||
| No, but it is strongly recommended, even if you don't use any Identity store or integration. | No, but it is strongly recommended, even if you don't use any Identity store or integration. | ||||||
|  |  | ||||||
| @@ -23,9 +35,6 @@ In its default configuration, mxisd uses other federated public servers when per | |||||||
| It can also [be configured](features/identity.md#lookups) to use the central matrix.org servers, giving you access to at | It can also [be configured](features/identity.md#lookups) to use the central matrix.org servers, giving you access to at | ||||||
| least the same information as if you were not running it. | least the same information as if you were not running it. | ||||||
|  |  | ||||||
| It will also give your users a choice to make their 3PIDs available publicly, ensuring they are made aware of the |  | ||||||
| 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 mxisd is like your gatekeeper and guardian angel. It does not change what you already know, just adds some nice | ||||||
| simple features on top of it. | simple features on top of it. | ||||||
|  |  | ||||||
| @@ -47,13 +56,14 @@ 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 mxisd concepts and vocabulary. | ||||||
|  |  | ||||||
| ### I already use the synapse LDAP3 auth provider. Why should I care about mxisd? | ### I already use the synapse LDAP3 auth provider. Why should I care about mxisd? | ||||||
| The [synapse LDAP3 auth provider](https://github.com/matrix-org/matrix-synapse-ldap3) is not longer maintained and | The [synapse LDAP3 auth provider](https://github.com/matrix-org/matrix-synapse-ldap3) is not longer maintained despite | ||||||
| only handles on specific flow: validate credentials at login. | saying so and only handles on specific flow: validate credentials at login. | ||||||
|  |  | ||||||
| It does not: | It does not: | ||||||
| - Auto-provision user profiles | - Auto-provision user profiles | ||||||
| - Integrate with Identity management | - Integrate with Identity management | ||||||
| - Integrate with Directory searches | - Integrate with Directory searches | ||||||
|  | - Integrate with Profile data | ||||||
|  |  | ||||||
| mxisd is a replacement and enhancement of it, offering coherent results in all areas, which the LDAP3 auth provider | mxisd is a replacement and enhancement of it, offering coherent results in all areas, which the LDAP3 auth provider | ||||||
| does not. | does not. | ||||||
| @@ -74,7 +84,7 @@ No. | |||||||
| In its default configuration, mxisd does not talk to the central Identity server matrix.org to avoid leaking your private | In its default configuration, mxisd does not talk to the central Identity server matrix.org to avoid leaking your private | ||||||
| data and those of people you might know. | data and those of people you might know. | ||||||
|  |  | ||||||
| mxisd [can be configured](features/identity.md#lookups) to talk to the central Identity servers if you wish. | [You can configure it](features/identity.md#lookups) to talk to the central Identity servers if you wish. | ||||||
|  |  | ||||||
| ### So mxisd is just a big hack! I don't want to use non-official features! | ### So mxisd is just a big hack! I don't want to use non-official features! | ||||||
| mxisd primary concerns are your privacy and to always be compatible with the Matrix ecosystem and the Identity service API.   | mxisd primary concerns are your privacy and to always be compatible with the Matrix ecosystem and the Identity service API.   | ||||||
|   | |||||||
| @@ -74,7 +74,15 @@ See your Identity store [documentation](../stores/README.md) on how to enable th | |||||||
|  |  | ||||||
|  |  | ||||||
| ## Advanced | ## 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 | ### Overview | ||||||
| This is performed by intercepting the Homeserver endpoint `/_matrix/client/r0/login` as depicted below: | This is performed by intercepting the Homeserver endpoint `/_matrix/client/r0/login` as depicted below: | ||||||
| @@ -109,10 +117,10 @@ Steps of user authentication using a 3PID: | |||||||
| 4. The response from the Homeserver is sent back to the client, believing it was the HS which directly answered. | 4. The response from the Homeserver is sent back to the client, believing it was the HS which directly answered. | ||||||
|  |  | ||||||
| ### Requirements | ### Requirements | ||||||
| - [Basic Authentication configured and working](#basic) |  | ||||||
| - Reverse proxy setup |  | ||||||
| - Homeserver |  | ||||||
| - Compatible [Identity store](../stores/README.md) | - 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 | ### Configuration | ||||||
| #### Reverse Proxy | #### Reverse Proxy | ||||||
| @@ -143,9 +151,12 @@ the internal IP of the Homeserver so it can talk to it directly to integrate its | |||||||
|  |  | ||||||
| To do so, put the following configuration in your mxisd configuration: | To do so, put the following configuration in your mxisd configuration: | ||||||
| ```yaml | ```yaml | ||||||
| dns.overwrite.homeserver.client: | dns: | ||||||
|   - name: 'example.org' |   overwrite: | ||||||
|     value: 'http://localhost:8008' |     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. | `name` must be the hostname of the URL that clients use when connecting to the Homeserver. | ||||||
| You can use `${server.name}` to auto-populate the `value` using the `server.name` 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. | ||||||
| @@ -153,3 +164,40 @@ In case the hostname is the same as your Matrix domain and `server.name` is not | |||||||
| `matrix.domain` and will still probably have the correct value. | `matrix.domain` and will still probably have the correct value. | ||||||
|  |  | ||||||
| `value` is the base internal URL of the Homeserver, without any `/_matrix/..` or trailing `/`. | `value` is the base internal URL of the Homeserver, without any `/_matrix/..` or trailing `/`. | ||||||
|  |  | ||||||
|  | #### Username rewrite | ||||||
|  | In mxisd 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' | ||||||
|  | ``` | ||||||
|   | |||||||
| @@ -5,16 +5,19 @@ to 3PID queries if no mapping was found, allowing seamless bridging to a network | |||||||
| This is performed by implementing a specific endpoint on the bridge to map a 3PID lookup to a virtual user. | This is performed by implementing a specific endpoint on the bridge to map a 3PID lookup to a virtual user. | ||||||
|  |  | ||||||
| **NOTE**: This document is incomplete and might be misleading. In doubt, come in our Matrix room.   | **NOTE**: This document is incomplete and might be misleading. In doubt, come in our Matrix room.   | ||||||
| You can also look at our [Email Bridge README](https://github.com/kamax-io/matrix-appservice-email#mxisd) for an example | You can also look at our [Email Bridge README](https://github.com/kamax-matrix/matrix-appservice-email#mxisd) for an example | ||||||
| of working configuration. | of working configuration. | ||||||
|  |  | ||||||
| ## Configuration | ## Configuration | ||||||
| ```yaml | ```yaml | ||||||
| lookup.recursive.bridge.enabled: <boolean> | lookup: | ||||||
| lookup.recursive.bridge.recursiveOnly: <boolean> |   recursive: | ||||||
| lookup.recursive.bridge.server: <URL to the bridge endpoint for all 3PID medium> |     bridge: | ||||||
| lookup.recursive.bridge.mappings: |       enabled: <boolean> | ||||||
|   <3PID MEDIUM HERE>: <URL to dedicated bridge for that medium> |       recursiveOnly: <boolean> | ||||||
|  |       server: <URL to the bridge endpoint for all 3PID medium> | ||||||
|  |       mappings: | ||||||
|  |         <3PID MEDIUM HERE>: <URL to dedicated bridge for that medium> | ||||||
|  |  | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
|   | |||||||
| @@ -123,27 +123,31 @@ the internal IP of the Homeserver so it can talk to it directly to integrate its | |||||||
|  |  | ||||||
| To do so, use the following configuration: | To do so, use the following configuration: | ||||||
| ```yaml | ```yaml | ||||||
| dns.overwrite.homeserver.client: | dns: | ||||||
|   - name: 'example.org' |   overwrite: | ||||||
|     value: 'http://localhost:8008' |     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.   | - `name` must be the hostname of the URL that clients use when connecting to the Homeserver. | ||||||
| In case the hostname is the same as your Matrix domain, you can use `${matrix.domain}` to auto-populate the value using | - `value` is the base internal URL of the Homeserver, without any `/_matrix/..` or trailing `/`. | ||||||
| the `matrix.domain` configuration option and avoid duplicating it. |  | ||||||
|  |  | ||||||
| `value` is the base internal URL of the Homeserver, without any `/_matrix/..` or trailing `/`. |  | ||||||
|  |  | ||||||
| ## Next steps | ## Next steps | ||||||
| ### Homeserver results | ### Homeserver results | ||||||
| You can configure if the Homeserver should be queried at all when doing a directory search.   | You can configure if the Homeserver should be queried at all when doing a directory search.   | ||||||
| To disable Homeserver results, set the following in mxisd configuration file: | To disable Homeserver results, set the following in mxisd configuration file: | ||||||
| ```yaml | ```yaml | ||||||
| directory.exclude.homeserver: true | directory: | ||||||
|  |   exclude: | ||||||
|  |     homeserver: true | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| ### 3PID exclusion in search | ### 3PID exclusion in search | ||||||
| You can configure if the 3PID should also be included when doing a directory search. | You can configure if the 3PID should also be included when doing a directory search. | ||||||
| By default, a search is performed on the 3PIDs. If you would like to not include them: | By default, a search is performed on the 3PIDs. If you would like to not include them: | ||||||
| ```yaml | ```yaml | ||||||
| directory.exclude.threepid: true | directory: | ||||||
|  |   exclude: | ||||||
|  |     threepid: true | ||||||
| ``` | ``` | ||||||
|   | |||||||
| @@ -1,12 +1,16 @@ | |||||||
| # Profile enhancement | # Profile | ||||||
| **WARNING**: Alpha feature, not officially supported. Do not use. | **WARNING**: The following sub-features are considered experimental and not officially supported. Use at your own peril. | ||||||
|  |  | ||||||
| This feature allows to enhance a profile query with more info than just Matrix ID and Display name, allowing for custom | ## Public Profile enhancement | ||||||
| applications to retrieve custom data not currently provided by synapse, per example. | 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. | ||||||
|  |  | ||||||
| ## Configuration | **WARNING**: This information can be queried without authentication as per the specification. Do not enable unless in a | ||||||
| ### Reverse proxy | controlled environment. | ||||||
| #### Apache |  | ||||||
|  | ### Configuration | ||||||
|  | #### Reverse proxy | ||||||
|  | ##### Apache | ||||||
| ```apache | ```apache | ||||||
| ProxyPassMatch "^/_matrix/client/r0/profile/([^/]+)$" "http://127.0.0.1:8090/_matrix/client/r0/profile/$1" | ProxyPassMatch "^/_matrix/client/r0/profile/([^/]+)$" "http://127.0.0.1:8090/_matrix/client/r0/profile/$1" | ||||||
| ``` | ``` | ||||||
|   | |||||||
| @@ -40,10 +40,21 @@ The port must be HTTPS capable which is what you get in a regular setup with a r | |||||||
| If you would like to disable outbound federation and isolate your identity server from the rest of the Matrix network, | If you would like to disable outbound federation and isolate your identity server from the rest of the Matrix network, | ||||||
| use the following mxisd configuration options: | use the following mxisd configuration options: | ||||||
| ```yaml | ```yaml | ||||||
| lookup.recursive.enabled: false | lookup: | ||||||
| invite.resolution.recursive: false |   recursive: | ||||||
| session.policy.validation.forLocal.toRemote.enabled: false |     enabled: false | ||||||
| session.policy.validation.forRemote.toRemote.enabled: false | invite: | ||||||
|  |   resolution: | ||||||
|  |     recursive: false | ||||||
|  | session: | ||||||
|  |   policy: | ||||||
|  |     validation: | ||||||
|  |       forLocal: | ||||||
|  |         toRemote: | ||||||
|  |           enabled: false | ||||||
|  |       forRemote: | ||||||
|  |         toRemote: | ||||||
|  |           enabled: false | ||||||
| ```  | ```  | ||||||
|  |  | ||||||
| There is currently no way to selectively disable federation towards specific servers, but this feature is planned. | There is currently no way to selectively disable federation towards specific servers, but this feature is planned. | ||||||
|   | |||||||
| @@ -1,14 +1,15 @@ | |||||||
| # Identity | # Identity | ||||||
| **WARNING**: This document is incomplete and can be missleading. | **WARNING**: This document is incomplete and can be misleading. | ||||||
|  |  | ||||||
| Implementation of the [Unofficial Matrix Identity Service API](https://kamax.io/matrix/api/identity_service/unstable.html). | Implementation of the [Identity Service API r0.1.0](https://matrix.org/docs/spec/identity_service/r0.1.0.html). | ||||||
|  |  | ||||||
| ## Lookups | ## Lookups | ||||||
| If you would like to use the central matrix.org Identity server to ensure maximum discovery at the cost of potentially | If you would like to use the central matrix.org Identity server to ensure maximum discovery at the cost of potentially | ||||||
| leaking all your contacts information, add the following to your configuration: | leaking all your contacts information, add the following to your configuration: | ||||||
| ```yaml | ```yaml | ||||||
| forward.servers: | forward: | ||||||
|   - 'matrix-org' |   servers: | ||||||
|  |     - 'matrix-org' | ||||||
| ``` | ``` | ||||||
| **NOTE:** You should carefully consider enabling this option, which is discouraged.   | **NOTE:** You should carefully consider enabling this option, which is discouraged.   | ||||||
| For more info, see the [relevant issue](https://github.com/kamax-matrix/mxisd/issues/76). | For more info, see the [relevant issue](https://github.com/kamax-matrix/mxisd/issues/76). | ||||||
|   | |||||||
							
								
								
									
										10
									
								
								docs/features/profile.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								docs/features/profile.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | |||||||
|  | # 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). | ||||||
|  |  | ||||||
|  | Currently supported: | ||||||
|  | - Display name | ||||||
|  | - 3PIDs | ||||||
|  | - Roles/Groups | ||||||
|  |  | ||||||
|  | Experimental sub-features are also available. See [the dedicated document](experimental/profile.md). | ||||||
| @@ -6,22 +6,34 @@ | |||||||
| 5. [Validate](#validate) | 5. [Validate](#validate) | ||||||
| 6. [Next steps](#next-steps) | 6. [Next steps](#next-steps) | ||||||
|  |  | ||||||
| Following these quick start instructions, you will have a basic setup that can perform recursive/federated lookups and | Following these quick start instructions, you will have a basic setup that can perform recursive/federated lookups.   | ||||||
| talk to the central Matrix.org Identity server.   |  | ||||||
| This will be a good ground work for further integration with features and your existing Identity stores. | This will be a good ground work for further integration with features and your existing Identity stores. | ||||||
|  |  | ||||||
|  | --- | ||||||
|  |  | ||||||
|  | If you would like a more fully integrated setup out of the box, the [matrix-docker-ansible-deploy](https://github.com/spantaleev/matrix-docker-ansible-deploy) | ||||||
|  | project provides a turn-key full-stack solution, including LDAP and the various mxisd features enabled and ready.   | ||||||
|  | We work closely with the project owner so the latest mxisd version is always supported. | ||||||
|  |  | ||||||
|  | If you choose to use it, this Getting Started guide is not applicable - See the project documentation. You may then | ||||||
|  | directly go to the [Next steps](#next-steps). | ||||||
|  |  | ||||||
| ## Preparation | ## Preparation | ||||||
| You will need: | You will need: | ||||||
| - Working Homeserver, ideally with working federation | - Working Homeserver, ideally with working federation | ||||||
| - Reverse proxy with regular TLS/SSL certificate (Let's encrypt) for your mxisd domain | - Reverse proxy with regular TLS/SSL certificate (Let's encrypt) for your mxisd domain | ||||||
|  |  | ||||||
| As synapse requires an HTTPS connection when talking to an Identity service, **a reverse proxy is required** as mxisd does | If you use synapse: | ||||||
| not support HTTPS listener at this time. | - It requires an HTTPS connection when talking to an Identity service, **a reverse proxy is required** as mxisd 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 mxisd 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 mxisd reachable via the same public hostname. | ||||||
|  |  | ||||||
| Be aware of a [NAT/Reverse proxy gotcha](https://github.com/kamax-matrix/mxisd/wiki/Gotchas#nating) if you use the same | Be aware of a [NAT/Reverse proxy gotcha](https://github.com/kamax-matrix/mxisd/wiki/Gotchas#nating) if you use the same | ||||||
| hostname. | host. | ||||||
|  |  | ||||||
| The following Quick Start guide assumes you will host the Homeserver and mxisd under the same hostname.   | The following Quick Start guide assumes you will host the Homeserver and mxisd under the same hostname.   | ||||||
| If you would like a high-level view of the infrastructure and how each feature is integrated, see the | If you would like a high-level view of the infrastructure and how each feature is integrated, see the | ||||||
| @@ -38,13 +50,20 @@ Install via: | |||||||
| See the [Latest release](https://github.com/kamax-matrix/mxisd/releases/latest) for links to each. | See the [Latest release](https://github.com/kamax-matrix/mxisd/releases/latest) for links to each. | ||||||
|  |  | ||||||
| ## Configure | ## Configure | ||||||
| **NOTE**: please view the install instruction for your platform, as this step might be optional or already handled for you. | > **NOTE**: Please view the install instruction for your platform, as this step might be optional or already handled for you. | ||||||
|  |    | ||||||
|  | > **NOTE**: Details about configuration syntax and format are described [here](configure.md) | ||||||
|  |  | ||||||
| Create/edit a minimal configuration (see installer doc for the location): | Create/edit a minimal configuration (see installer doc for the location): | ||||||
| ```yaml | ```yaml | ||||||
| matrix.domain: 'example.org' | matrix: | ||||||
| key.path: '/path/to/signing.key.file' |   domain: 'example.org' | ||||||
| storage.provider.sqlite.database: '/path/to/mxisd.db' | key: | ||||||
|  |   path: '/path/to/signing.key.file' | ||||||
|  | storage: | ||||||
|  |   provider: | ||||||
|  |     sqlite: | ||||||
|  |       database: '/path/to/mxisd.db' | ||||||
| ```   | ```   | ||||||
| - `matrix.domain` should be set to your Homeserver domain (`server_name` in synapse configuration) | - `matrix.domain` should be set to your Homeserver domain (`server_name` in synapse configuration) | ||||||
| - `key.path` will store the signing keys, which must be kept safe! If the file does not exist, keys will be generated for you. | - `key.path` will store the signing keys, which must be kept safe! If the file does not exist, keys will be generated for you. | ||||||
| @@ -67,7 +86,7 @@ ProxyPass /_matrix/identity http://0.0.0.0:8090/_matrix/identity | |||||||
| Typical configuration would look like: | Typical configuration would look like: | ||||||
| ```apache | ```apache | ||||||
| <VirtualHost *:443> | <VirtualHost *:443> | ||||||
|     ServerName example.org |     ServerName matrix.example.org | ||||||
|      |      | ||||||
|     ... |     ... | ||||||
|      |      | ||||||
| @@ -91,7 +110,7 @@ Typical configuration would look like: | |||||||
| ```nginx | ```nginx | ||||||
| server { | server { | ||||||
|     listen 443 ssl; |     listen 443 ssl; | ||||||
|     server_name example.org; |     server_name matrix.example.org; | ||||||
|      |      | ||||||
|     ... |     ... | ||||||
|      |      | ||||||
| @@ -114,17 +133,17 @@ Add your mxisd domain into the `homeserver.yaml` at `trusted_third_party_id_serv | |||||||
| In a typical configuration, you would end up with something similar to: | In a typical configuration, you would end up with something similar to: | ||||||
| ```yaml | ```yaml | ||||||
| trusted_third_party_id_servers: | trusted_third_party_id_servers: | ||||||
|     - example.org |     - matrix.example.org | ||||||
| ``` | ``` | ||||||
| It is recommended to remove `matrix.org` and `vector.im` (or any other default entry) from your configuration so only | It is **highly recommended** to remove `matrix.org` and `vector.im` (or any other default entry) from your configuration | ||||||
| your own Identity server is authoritative for your HS. | so only your own Identity server is authoritative for your HS. | ||||||
|  |  | ||||||
| ## Validate | ## Validate | ||||||
| **NOTE:** In case your homeserver has no working federation, step 5 will not happen. If step 4 took place, consider | **NOTE:** In case your homeserver has no working federation, step 5 will not happen. If step 4 took place, consider | ||||||
| your installation validated. | your installation validated. | ||||||
|  |  | ||||||
| 1. Log in using your Matrix client and set `https://example.org` as your Identity server URL, replacing `example.org` by | 1. Log in using your Matrix client and set `https://matrix.example.org` as your Identity server URL, replacing `matrix.example.org` | ||||||
| the relevant hostname which you configured in your reverse proxy. | by the relevant hostname which you configured in your reverse proxy. | ||||||
| 2. Create a new empty room. All further actions will take place in this room. | 2. Create a new empty room. All further actions will take place in this room. | ||||||
| 3. Invite `mxisd-federation-test@kamax.io` | 3. Invite `mxisd-federation-test@kamax.io` | ||||||
| 4. The 3PID invite should be turned into a Matrix invite to `@mxisd-lookup-test:kamax.io`. | 4. The 3PID invite should be turned into a Matrix invite to `@mxisd-lookup-test:kamax.io`. | ||||||
|   | |||||||
| @@ -2,37 +2,43 @@ | |||||||
| ## Instructions | ## Instructions | ||||||
| Follow the [build instructions](../build.md) then: | Follow the [build instructions](../build.md) then: | ||||||
|  |  | ||||||
| 1. Prepare files and directories: | ### Prepare files and directories: | ||||||
| ```bash | ```bash | ||||||
| # Create a dedicated user | # Create a dedicated user | ||||||
| useradd -r mxisd | useradd -r mxisd | ||||||
|  |  | ||||||
| # Create bin directory |  | ||||||
| mkdir /opt/mxisd |  | ||||||
|  |  | ||||||
| # Create config directory and set ownership | # Create config directory and set ownership | ||||||
| mkdir -p /etc/opt/mxisd | mkdir -p /etc/mxisd | ||||||
| chown -R mxisd /etc/opt/mxisd |  | ||||||
|  |  | ||||||
| # Create data directory and set ownership | # Create data directory and set ownership | ||||||
| mkdir -p /var/opt/mxisd | mkdir -p /var/lib/mxisd | ||||||
| chown -R mxisd /var/opt/mxisd | chown -R mxisd /var/lib/mxisd | ||||||
|  |  | ||||||
| # Copy <repo root>/build/libs/mxisd.jar to bin directory | # Create bin directory, copy the jar and launch scriot to bin directory | ||||||
| cp ./build/libs/mxisd.jar /opt/mxisd/ | mkdir /usr/lib/mxisd | ||||||
| chown mxisd /opt/mxisd/mxisd.jar | cp ./build/libs/mxisd.jar /usr/lib/mxisd/ | ||||||
| chmod a+x /opt/mxisd/mxisd.jar | cp ./src/script/mxisd /usr/lib/mxisd | ||||||
|  | chown -R mxisd /usr/lib/mxisd | ||||||
|  | chmod a+x /usr/lib/mxisd/mxisd | ||||||
|  |  | ||||||
| # Create symlink for easy exec | # Create symlink for easy exec | ||||||
| ln -s /opt/mxisd/mxisd.jar /usr/bin/mxisd | ln -s /usr/lib/mxisd/mxisd /usr/bin/mxisd | ||||||
| ``` | ``` | ||||||
| 2. Copy the sample config file `./application.example.yaml` to `/etc/opt/mxisd/mxisd.yaml`, edit to your needs |  | ||||||
| 3. Copy `src/systemd/mxisd.service` to `/etc/systemd/system/` and edit if needed | ### Prepare config file | ||||||
| 4. Enable service for auto-startup | Copy the sample config file `./mxisd.example.yaml` to `/etc/mxisd/mxisd.yaml`, edit to your needs | ||||||
|  |  | ||||||
|  | ### Prepare Systemd | ||||||
|  | 1. Copy `src/systemd/mxisd.service` to `/etc/systemd/system/` and edit if needed | ||||||
|  | 2. Enable service for auto-startup | ||||||
| ```bash | ```bash | ||||||
| systemctl enable mxisd | systemctl enable mxisd | ||||||
| ``` | ``` | ||||||
| 5. Start mxisd |  | ||||||
|  | ### Run | ||||||
| ```bash | ```bash | ||||||
| systemctl start mxisd | systemctl start mxisd | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
|  | ## Debug | ||||||
|  | mxisd logs to stdout, which is normally sent to `/var/log/syslog` or `/var/log/messages`. | ||||||
|   | |||||||
| @@ -39,7 +39,7 @@ | |||||||
| | [Authentication](../features/authentication.md) | Yes       | | | [Authentication](../features/authentication.md) | Yes       | | ||||||
| | [Directory](../features/directory.md)           | Yes       | | | [Directory](../features/directory.md)           | Yes       | | ||||||
| | [Identity](../features/identity.md)             | Yes       | | | [Identity](../features/identity.md)             | Yes       | | ||||||
| | [Profile](#profile)                             | Yes       | | | [Profile](../features/profile.md)               | Yes       | | ||||||
|  |  | ||||||
| This Identity Store lets you run arbitrary commands to handle the various requests in each support feature.   | This Identity Store lets you run arbitrary commands to handle the various requests in each support feature.   | ||||||
| It is the most versatile Identity store of mxisd, allowing you to connect any kind of logic with any executable/script. | It is the most versatile Identity store of mxisd, allowing you to connect any kind of logic with any executable/script. | ||||||
| @@ -68,7 +68,8 @@ We will use the term `Executable` for each lookup/action and `Processor` for eac | |||||||
|  |  | ||||||
| ### Global | ### Global | ||||||
| ```yaml | ```yaml | ||||||
| exec.enabled: <boolean> | exec: | ||||||
|  |   enabled: <boolean> | ||||||
| ``` | ``` | ||||||
| Enable/disable the Identity store at a global/default level. Each feature can still be individually enabled/disabled. | Enable/disable the Identity store at a global/default level. Each feature can still be individually enabled/disabled. | ||||||
|  |  | ||||||
| @@ -79,7 +80,9 @@ Not all features use all tokens, and each feature might also have its own specif | |||||||
| They can be set within the following scope: | They can be set within the following scope: | ||||||
|  |  | ||||||
| ```yaml | ```yaml | ||||||
| exec.token.<token>: '<value>' | exec: | ||||||
|  |   token: | ||||||
|  |     <token>: '<value>' | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| --- | --- | ||||||
| @@ -184,16 +187,19 @@ The following types are available: | |||||||
| ### Examples | ### Examples | ||||||
| #### Basic | #### Basic | ||||||
| ```yaml | ```yaml | ||||||
| exec.auth.enabled: true | exec: | ||||||
| exec.auth.command: '/opt/mxisd-exec/auth.sh' |   auth: | ||||||
| exec.auth.args: ['{localpart}'] |     enabled: true | ||||||
| exec.auth.input.type: 'plain' |     command: '/opt/mxisd-exec/auth.sh' | ||||||
| exec.auth.input.template: '{password}' |     args: ['{localpart}'] | ||||||
| exec.auth.env: |     input: | ||||||
|   DOMAIN: '{domain}' |       type: 'plain' | ||||||
|  |       template: '{password}' | ||||||
|  |   env: | ||||||
|  |     DOMAIN: '{domain}' | ||||||
| ``` | ``` | ||||||
| With Authentication enabled, run `/opt/mxisd-exec/auth.sh` when validating credentials, providing: | With Authentication enabled, run `/opt/mxisd-exec/auth.sh` when validating credentials, providing: | ||||||
| - A single command-line argument to provide the `localoart` as username  | - A single command-line argument to provide the `localpart` as username  | ||||||
| - A plain text string with the password token for standard input, which will be replaced by the password to check | - A plain text string with the password token for standard input, which will be replaced by the password to check | ||||||
| - A single environment variable `DOMAIN` containing Matrix ID domain, if given | - A single environment variable `DOMAIN` containing Matrix ID domain, if given | ||||||
|  |  | ||||||
| @@ -201,7 +207,7 @@ The command will use the default values for: | |||||||
| - Success exit status of `0` | - Success exit status of `0` | ||||||
| - Failure exit status of `1` | - Failure exit status of `1` | ||||||
| - Any other exit status considered as error | - Any other exit status considered as error | ||||||
| - The standard output processing as not processed | - Standard output will not be processed | ||||||
|  |  | ||||||
| #### Advanced | #### Advanced | ||||||
| Given the fictional `placeholder` feature: | Given the fictional `placeholder` feature: | ||||||
| @@ -243,14 +249,17 @@ See each dedicated [Feature](#features) section. | |||||||
| ## Authentication | ## Authentication | ||||||
| The Authentication feature can be enabled/disabled using: | The Authentication feature can be enabled/disabled using: | ||||||
| ```yaml | ```yaml | ||||||
| exec.auth.enabled: <true/false> | exec: | ||||||
|  |   auth: | ||||||
|  |     enabled: <true/false> | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| --- | --- | ||||||
|  |  | ||||||
| This feature provides a single *Executable* under the namespace: | This feature provides a single *Executable* under the namespace: | ||||||
| ```yaml | ```yaml | ||||||
| exec.auth: | exec: | ||||||
|  |   auth: | ||||||
|   ... |   ... | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| @@ -294,7 +303,9 @@ Default template: | |||||||
| ## Directory | ## Directory | ||||||
| The Directory feature can be enabled/disabled using: | The Directory feature can be enabled/disabled using: | ||||||
| ```yaml | ```yaml | ||||||
| exec.directory.enabled: <true/false> | exec: | ||||||
|  |   directory: | ||||||
|  |     enabled: <true/false> | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| --- | --- | ||||||
| @@ -303,13 +314,19 @@ Two search types configuration namespace are available, using the same input/out | |||||||
|  |  | ||||||
| By name: | By name: | ||||||
| ```yaml | ```yaml | ||||||
| exec.directory.search.byName: | exec: | ||||||
|   ... |   directory: | ||||||
|  |     search: | ||||||
|  |       byName: | ||||||
|  |         ... | ||||||
| ``` | ``` | ||||||
| By 3PID: | By 3PID: | ||||||
| ```yaml | ```yaml | ||||||
| exec.directory.search.byThreepid: | exec: | ||||||
|   ... |   directory: | ||||||
|  |     search: | ||||||
|  |       byThreepid: | ||||||
|  |         ... | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| #### Tokens | #### Tokens | ||||||
| @@ -386,8 +403,11 @@ The User ID type will default to `localpart` if: | |||||||
| ### Bulk lookup | ### Bulk lookup | ||||||
| Configuration namespace: | Configuration namespace: | ||||||
| ```yaml | ```yaml | ||||||
| exec.identity.lookup.bulk: | exec: | ||||||
|   ... |   identity: | ||||||
|  |     lookup: | ||||||
|  |       bulk: | ||||||
|  |         ... | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| #### Tokens | #### Tokens | ||||||
| @@ -418,7 +438,9 @@ Same as the [REST Identity Store](rest.md). | |||||||
| ## Profile | ## Profile | ||||||
| The Profile feature can be enabled/disabled using: | The Profile feature can be enabled/disabled using: | ||||||
| ```yaml | ```yaml | ||||||
| exec.profile.enabled: <true/false> | exec: | ||||||
|  |   profile: | ||||||
|  |     enabled: <true/false> | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| --- | --- | ||||||
| @@ -427,20 +449,26 @@ The following *Executable*s namespace are available, share the same input/output | |||||||
|  |  | ||||||
| Get Display name: | Get Display name: | ||||||
| ```yaml | ```yaml | ||||||
| exec.profile.displayName: | exec: | ||||||
|   ... |   profile: | ||||||
|  |     displayName: | ||||||
|  |       ... | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| Get 3PIDs: | Get 3PIDs: | ||||||
| ```yaml | ```yaml | ||||||
| exec.profile.threePid: | exec: | ||||||
|   ... |   profile: | ||||||
|  |     threePid: | ||||||
|  |       ... | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| Get Roles: | Get Roles: | ||||||
| ```yaml | ```yaml | ||||||
| exec.profile.role: | exec: | ||||||
|   ... |   profile: | ||||||
|  |     role: | ||||||
|  |       ... | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -2,12 +2,12 @@ | |||||||
| https://firebase.google.com/ | https://firebase.google.com/ | ||||||
|  |  | ||||||
| ## Features | ## Features | ||||||
| |      Name      | Supported? | | |                       Name                      | Supported | | ||||||
| |----------------|------------| | |-------------------------------------------------|-----------| | ||||||
| | Authentication | Yes        | | | [Authentication](../features/authentication.md) | Yes       | | ||||||
| | Directory      | No         | | | [Directory](../features/directory.md)           | No        | | ||||||
| | Identity       | Yes        | | | [Identity](../features/identity.md)             | Yes       | | ||||||
| | Profile        | No         | | | [Profile](../features/profile.md)               | No        | | ||||||
|  |  | ||||||
| ## Requirements | ## Requirements | ||||||
| This backend requires a suitable Matrix client capable of performing Firebase authentication and passing the following | This backend requires a suitable Matrix client capable of performing Firebase authentication and passing the following | ||||||
| @@ -19,35 +19,41 @@ If your client is Riot, you will need a custom version. | |||||||
|  |  | ||||||
| ## Configuration | ## Configuration | ||||||
| ```yaml | ```yaml | ||||||
| firebase.enabled: <boolean> | firebase: | ||||||
|  |   enabled: <boolean> | ||||||
| ``` | ``` | ||||||
| Enable/disable this identity store. | Enable/disable this identity store. | ||||||
|  |  | ||||||
| Example: | Example: | ||||||
| ```yaml | ```yaml | ||||||
| firebase.enabled: <boolean> | firebase: | ||||||
|  |   enabled: <boolean> | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| --- | --- | ||||||
|  |  | ||||||
| ```yaml | ```yaml | ||||||
| firebase.credentials: <string> | firebase: | ||||||
|  |   credentials: <string> | ||||||
| ``` | ``` | ||||||
| Path to the credentials file provided by Google Firebase to use with an external app. | Path to the credentials file provided by Google Firebase to use with an external app. | ||||||
|  |  | ||||||
| Example: | Example: | ||||||
| ```yaml | ```yaml | ||||||
| firebase.credentials: '/path/to/firebase/credentials.json' | firebase: | ||||||
|  |   credentials: '/path/to/firebase/credentials.json' | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| --- | --- | ||||||
|  |  | ||||||
| ```yaml | ```yaml | ||||||
| firebase.database: <string> | firebase: | ||||||
|  |   database: <string> | ||||||
| ``` | ``` | ||||||
| URL to your Firebase database. | URL to your Firebase database. | ||||||
|  |  | ||||||
| Example: | Example: | ||||||
| ```yaml | ```yaml | ||||||
| firebase.database: 'https://my-project.firebaseio.com/' | firebase: | ||||||
|  |   database: 'https://my-project.firebaseio.com/' | ||||||
| ``` | ``` | ||||||
|   | |||||||
| @@ -8,37 +8,45 @@ | |||||||
| For NetIQ, replace all the `ldap` prefix in the configuration by `netiq`. | For NetIQ, replace all the `ldap` prefix in the configuration by `netiq`. | ||||||
|  |  | ||||||
| ## Features | ## Features | ||||||
| |      Name      | Supported? | | |                       Name                      | Supported | | ||||||
| |----------------|------------| | |-------------------------------------------------|-----------| | ||||||
| | Authentication | Yes        | | | [Authentication](../features/authentication.md) | Yes       | | ||||||
| | Directory      | Yes        | | | [Directory](../features/directory.md)           | Yes       | | ||||||
| | Identity       | Yes        | | | [Identity](../features/identity.md)             | Yes       | | ||||||
| | Profile        | Yes        | | | [Profile](../features/profile.md)               | Yes       | | ||||||
|  |  | ||||||
| ## Getting started | ## Getting started | ||||||
| ### Base | ### Base | ||||||
| To use your LDAP backend, add the bare minimum configuration in mxisd config file: | To use your LDAP backend, add the bare minimum configuration in mxisd config file: | ||||||
| ```yaml | ```yaml | ||||||
| ldap.enabled: true | ldap: | ||||||
| ldap.connection.host: 'ldapHostnameOrIp' |   enabled: true | ||||||
| ldap.connection.port: 389 |   connection: | ||||||
| ldap.connection.bindDn: 'CN=My Mxisd User,OU=Users,DC=example,DC=org' |     host: 'ldapHostnameOrIp' | ||||||
| ldap.connection.bindPassword: 'TheUserPassword' |     port: 389 | ||||||
| ldap.connection.baseDn: 'OU=Users,DC=example,DC=org' |     bindDn: 'CN=My Mxisd 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. mxisd 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 | ### TLS/SSL connection | ||||||
| If you would like to use a TLS/SSL connection, use the following configuration options (STARTLS not supported): | If you would like to use a TLS/SSL connection, use the following configuration options (STARTLS not supported): | ||||||
| ```yaml | ```yaml | ||||||
| ldap.connection.tls: true | ldap: | ||||||
| ldap.connection.port: 12345 |   connection: | ||||||
|  |     tls: true | ||||||
|  |     port: 12345 | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| ### Filter results | ### Filter results | ||||||
| You can also set a default global filter on any LDAP queries: | You can also set a default global filter on any LDAP queries: | ||||||
| ```yaml | ```yaml | ||||||
| ldap.filter: '(memberOf=CN=My Matrix Users,OU=Groups,DC=example,DC=org)' | ldap: | ||||||
|  |   filter: '(memberOf=CN=My Matrix Users,OU=Groups,DC=example,DC=org)' | ||||||
| ``` | ``` | ||||||
| This example would only return users part of the group called `My Matrix Users`. | This example would only return users part of the group called `My Matrix Users`. | ||||||
| This can be overwritten or append in each specific flow describe below. | This can be overwritten or append in each specific flow describe below. | ||||||
| @@ -61,8 +69,11 @@ most certainly configure those mappings. | |||||||
|  |  | ||||||
| The following example would set the `sAMAccountName` attribute as a Matrix User ID localpart: | The following example would set the `sAMAccountName` attribute as a Matrix User ID localpart: | ||||||
| ```yaml | ```yaml | ||||||
| ldap.attribute.uid.type: 'uid' | ldap: | ||||||
| ldap.attribute.uid.value: 'sAMAccountName' |   attribute: | ||||||
|  |     uid: | ||||||
|  |       type: 'uid' | ||||||
|  |       value: 'sAMAccountName' | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| #### Display name | #### Display name | ||||||
| @@ -70,7 +81,9 @@ Use `ldap.attribute.name`. | |||||||
|  |  | ||||||
| The following example would set the display name to the value of the `cn` attribute: | The following example would set the display name to the value of the `cn` attribute: | ||||||
| ```yaml | ```yaml | ||||||
| ldap.attribute.name: 'cn' | ldap: | ||||||
|  |   attribute: | ||||||
|  |     name: 'cn' | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| #### 3PIDs | #### 3PIDs | ||||||
| @@ -79,13 +92,15 @@ 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/resources/application.yaml#L67) | ||||||
| for emails and phone number: | for emails and phone number: | ||||||
| ```yaml | ```yaml | ||||||
| ldap.attribute.threepid.email: | ldap: | ||||||
|   - 'mail' |   attribute: | ||||||
|   - 'otherMailAttribute' |     threepid: | ||||||
|  |       email: | ||||||
| ldap.attribute.threepid.msisdn: |         - 'mail' | ||||||
|   - 'phone' |         - 'otherMailAttribute' | ||||||
|   - 'otherPhoneAttribute' |       msisdn: | ||||||
|  |         - 'phone' | ||||||
|  |         - 'otherPhoneAttribute' | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| ## Features | ## Features | ||||||
| @@ -98,24 +113,29 @@ configuration item is needed to get started. | |||||||
| - `ldap.identity.medium`: Namespace to overwrite generated queries from the list of attributes for each 3PID medium. | - `ldap.identity.medium`: Namespace to overwrite generated queries from the list of attributes for each 3PID medium. | ||||||
|  |  | ||||||
| ### Authentication | ### Authentication | ||||||
| No further configuration is needed to use the Authentication feature with LDAP once globally enabled and configured. | After you have configured and enabled the [feature itself](../features/authentication.md), no further configuration is | ||||||
|  | needed with this identity store to make it work. | ||||||
|  |  | ||||||
| Profile auto-fill is enabled by default. It will use the `ldap.attribute.name` and `ldap.attribute.threepid` configuration | Profile auto-fill is enabled by default. It will use the `ldap.attribute.name` and `ldap.attribute.threepid` configuration | ||||||
| options to get a lit of attributes to be used to build the user profile to pass on to synapse during authentication. | options to get a lit of attributes to be used to build the user profile to pass on to synapse during authentication. | ||||||
|  |  | ||||||
| #### Configuration | #### Configuration | ||||||
| - `ldap.auth.filter`: Specific user filter applied during identity search. Global filter is used if blank/not set. | - `ldap.auth.filter`: Specific user filter applied during username search. Global filter is used if blank/not set. | ||||||
|  |  | ||||||
| ### Directory | ### Directory | ||||||
| No further configuration is needed to use the Directory feature with LDAP once globally enabled and configured. | After you have configured and enabled the [feature itself](../features/directory.md), no further configuration is | ||||||
|  | needed with this identity store to make it work. | ||||||
|  |  | ||||||
| #### Configuration | #### Configuration | ||||||
| To set a specific filter applied during directory search, use `ldap.directory.filter` | To set a specific filter applied during directory search, use `ldap.directory.filter` | ||||||
|  |  | ||||||
| If you would like to use extra attributes in search that are not 3PIDs, like nicknames, group names, employee number: | If you would like to use extra attributes in search that are not 3PIDs, like nicknames, group names, employee number: | ||||||
| ```yaml | ```yaml | ||||||
| ldap.directory.attribute.other: | ldap: | ||||||
|   - 'myNicknameAttribute' |   directory: | ||||||
|   - 'memberOf' |     attribute: | ||||||
|   - 'employeeNumberAttribute' |       other: | ||||||
|  |         - 'myNicknameAttribute' | ||||||
|  |         - 'memberOf' | ||||||
|  |         - 'employeeNumberAttribute' | ||||||
| ``` | ``` | ||||||
|   | |||||||
| @@ -3,38 +3,45 @@ The REST backend allows you to query identity data in existing webapps, like: | |||||||
| - Forums (phpBB, Discourse, etc.) | - Forums (phpBB, Discourse, etc.) | ||||||
| - Custom Identity stores (Keycloak, ...) | - Custom Identity stores (Keycloak, ...) | ||||||
| - CRMs (Wordpress, ...) | - 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 | ## Features | ||||||
| |      Name      | Supported? | | | Name                                            | Supported? | | ||||||
| |----------------|------------| | |-------------------------------------------------|------------| | ||||||
| | Authentication | Yes        | | | [Authentication](../features/authentication.md) | Yes        | | ||||||
| | Directory      | Yes        | | | [Directory](../features/directory.md)           | Yes        | | ||||||
| | Identity       | Yes        | | | [Identity](../features/identity.md)             | Yes        | | ||||||
| | Profile        | No         | | | [Profile](../features/profile.md)               | Yes        | | ||||||
|  |  | ||||||
| ## Configuration | ## Configuration | ||||||
| | Key                              | Default                                        | Description                                          | | | Key                                  | Default                                        | Description                                          | | ||||||
| |----------------------------------|------------------------------------------------|------------------------------------------------------| | |--------------------------------------|------------------------------------------------|------------------------------------------------------| | ||||||
| | `rest.enabled`                   | `false`                                        | Globally enable/disable the REST backend             | | | `rest.enabled`                       | `false`                                        | Globally enable/disable the REST backend             | | ||||||
| | `rest.host`                      | *None*                                        | Default base URL to use for the different endpoints. | | | `rest.host`                          | *None*                                         | Default base URL to use for the different endpoints. | | ||||||
| | `rest.endpoints.auth`            | `/_mxisd/backend/api/v1/auth/login`            | Validate credentials and get user profile            | | | `rest.endpoints.auth`                | `/_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.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.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.identity.bulk`       | `/_mxisd/backend/api/v1/identity/bulk`         | Endpoint to query a list of 3PID                     | | ||||||
|  | | `rest.endpoints.profile.displayName` | `/_mxisd/backend/api/v1/profile/displayName`   | Query the display name for a Matrix ID | ||||||
|  | | `rest.endpoints.profile.threepids`   | `/_mxisd/backend/api/v1/profile/threepids`     | Query the 3PIDs for a Matrix ID | ||||||
|  | | `rest.endpoints.profile.roles`       | `/_mxisd/backend/api/v1/profile/roles`         | Query the Roles for a Matrix ID | ||||||
|  |  | ||||||
| Endpoint values can handle two formats: | Endpoint values can handle two formats: | ||||||
| - URL Path starting with `/` that gets happened to the `rest.host` | - URL Path starting with `/` that gets happened to the `rest.host` | ||||||
| - Full URL, if you want each endpoint to go to a specific server/protocol/port | - 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. | `rest.host` is mandatory if at least one endpoint is not a full URL. | ||||||
|  |  | ||||||
| ## Endpoints | ## Endpoints | ||||||
| ### Authentication | ### Authentication | ||||||
| HTTP method: `POST`   | - Method: `POST` | ||||||
| Content-type: JSON UTF-8 | - Content-Type: `application/json` (JSON) | ||||||
|  | - Encoding: `UTF8` | ||||||
|    |    | ||||||
| #### Request Body | #### Request Body | ||||||
| ```json | ```json | ||||||
| @@ -87,8 +94,9 @@ If the authentication succeed: | |||||||
| ``` | ``` | ||||||
|  |  | ||||||
| ### Directory | ### Directory | ||||||
| HTTP method: `POST` | - Method: `POST` | ||||||
| Content-type: JSON UTF-8 | - Content-Type: `application/json` (JSON) | ||||||
|  | - Encoding: `UTF8` | ||||||
|  |  | ||||||
| #### Request Body | #### Request Body | ||||||
| ```json | ```json | ||||||
| @@ -113,7 +121,7 @@ If users found: | |||||||
|       "user_id": "UserIdLocalpart" |       "user_id": "UserIdLocalpart" | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       ... |       "...": "..." | ||||||
|     } |     } | ||||||
|   ] |   ] | ||||||
| } | } | ||||||
| @@ -129,10 +137,11 @@ If no user found: | |||||||
|  |  | ||||||
| ### Identity | ### Identity | ||||||
| #### Single 3PID lookup | #### Single 3PID lookup | ||||||
| HTTP method: `POST`   | - Method: `POST` | ||||||
| Content-type: JSON UTF-8   | - Content-Type: `application/json` (JSON) | ||||||
|  | - Encoding: `UTF8` | ||||||
|    |    | ||||||
| #### Request Body | ##### Request Body | ||||||
| ```json | ```json | ||||||
| { | { | ||||||
|   "lookup": { |   "lookup": { | ||||||
| @@ -142,7 +151,7 @@ Content-type: JSON UTF-8 | |||||||
| } | } | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| #### Response Body | ##### Response Body | ||||||
| If a match was found: | If a match was found: | ||||||
| - `lookup.id.type` supported values: `localpart`, `mxid` | - `lookup.id.type` supported values: `localpart`, `mxid` | ||||||
| ```json | ```json | ||||||
| @@ -164,10 +173,11 @@ If no match was found: | |||||||
| ``` | ``` | ||||||
|  |  | ||||||
| #### Bulk 3PID lookup | #### Bulk 3PID lookup | ||||||
| HTTP method: `POST`   | - Method: `POST` | ||||||
| Content-type: JSON UTF-8   | - Content-Type: `application/json` (JSON) | ||||||
|  | - Encoding: `UTF8` | ||||||
|    |    | ||||||
| #### Request Body | ##### Request Body | ||||||
| ```json | ```json | ||||||
| { | { | ||||||
|   "lookup": [ |   "lookup": [ | ||||||
| @@ -183,7 +193,7 @@ Content-type: JSON UTF-8 | |||||||
| } | } | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| #### Response Body | ##### Response Body | ||||||
| For all entries where a match was found: | For all entries where a match was found: | ||||||
| - `lookup[].id.type` supported values: `localpart`, `mxid` | - `lookup[].id.type` supported values: `localpart`, `mxid` | ||||||
| ```json | ```json | ||||||
| @@ -215,3 +225,53 @@ If no match was found: | |||||||
|   "lookup": [] |   "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,28 +6,30 @@ | |||||||
| - SQLite | - SQLite | ||||||
|  |  | ||||||
| ## Features | ## Features | ||||||
| |      Name      | Supported? | | |                       Name                      | Supported | | ||||||
| |----------------|------------| | |-------------------------------------------------|-----------| | ||||||
| | Authentication | No         | | | [Authentication](../features/authentication.md) | No        | | ||||||
| | Directory      | Yes        | | | [Directory](../features/directory.md)           | Yes       | | ||||||
| | Identity       | Yes        | | | [Identity](../features/identity.md)             | Yes       | | ||||||
| | Profile        | Yes        | | | [Profile](../features/profile.md)               | Yes       | | ||||||
|  |  | ||||||
| Due to the implementation complexity of supporting arbitrary hashing/encoding mechanisms or auth flow, Authentication | Due to the implementation complexity of supporting arbitrary hashing/encoding mechanisms or auth flow, Authentication | ||||||
| will be out of scope of SQL Identity stores and should be done via one of the other identity stores, typically | will be out of scope of SQL Identity stores and should be done via one of the other identity stores, typically | ||||||
| the [REST Identity store](rest.md). | the [Exec Identity Store](exec.md) or the [REST Identity Store](rest.md). | ||||||
|  |  | ||||||
| ## Configuration | ## Configuration | ||||||
| ### Basic | ### Basic | ||||||
| ```yaml | ```yaml | ||||||
| sql.enabled: <boolean> | sql: | ||||||
|  |   enabled: <boolean> | ||||||
| ``` | ``` | ||||||
| Enable/disable the identity store | Enable/disable the identity store | ||||||
|  |  | ||||||
| --- | --- | ||||||
|  |  | ||||||
| ```yaml | ```yaml | ||||||
| sql.type: <string> | sql: | ||||||
|  |   type: <string> | ||||||
| ``` | ``` | ||||||
| Set the SQL backend to use: | Set the SQL backend to use: | ||||||
| - `sqlite` | - `sqlite` | ||||||
| @@ -38,14 +40,16 @@ Set the SQL backend to use: | |||||||
| ### Connection | ### Connection | ||||||
| #### SQLite | #### SQLite | ||||||
| ```yaml | ```yaml | ||||||
| sql.connection: <string> | sql: | ||||||
|  |   connection: <string> | ||||||
| ``` | ``` | ||||||
| Set the value to the absolute path to the Synapse SQLite DB file. | Set the value to the absolute path to the Synapse SQLite DB file. | ||||||
| Example: `/path/to/sqlite/file.db` | Example: `/path/to/sqlite/file.db` | ||||||
|  |  | ||||||
| #### Others | #### Others | ||||||
| ```yaml | ```yaml | ||||||
| sql.connection: //<HOST[:PORT]/DB?user=USER&password=PASS | sql: | ||||||
|  |   connection: //<HOST[:PORT]/DB?user=USER&password=PASS | ||||||
| ``` | ``` | ||||||
| Set the connection info for the database by replacing the following values: | Set the connection info for the database by replacing the following values: | ||||||
| - `HOST`: Hostname of the SQL server | - `HOST`: Hostname of the SQL server | ||||||
| @@ -58,20 +62,23 @@ This follow the JDBC URI syntax. See [official website](https://docs.oracle.com/ | |||||||
|  |  | ||||||
| ### Directory | ### Directory | ||||||
| ```yaml | ```yaml | ||||||
| sql.directory.enabled: false | sql: | ||||||
|  |   directory: | ||||||
|  |     enabled: false | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
|  |  | ||||||
| --- | --- | ||||||
|  |  | ||||||
| ```yaml | ```yaml | ||||||
| sql.directory.query: | sql: | ||||||
|   name: |   directory: | ||||||
|     type: <string> |     query: | ||||||
|     value: <string> |       name: | ||||||
|   threepid: |         type: <string> | ||||||
|     type: <string> |         value: <string> | ||||||
|     value: <string> |       threepid: | ||||||
|  |         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 mxisd how to process the ID column: | ||||||
| - `localpart` will append the `matrix.domain` to it | - `localpart` will append the `matrix.domain` to it | ||||||
| @@ -83,17 +90,21 @@ For each query, `type` can be used to tell mxisd how to process the ID column: | |||||||
|  |  | ||||||
| Example: | Example: | ||||||
| ```yaml | ```yaml | ||||||
| sql.directory.query: | sql: | ||||||
|   name: |   directory: | ||||||
|     type: 'localpart' |     query: | ||||||
|     value: 'SELECT idColumn, displayNameColumn FROM table WHERE displayNameColumn LIKE ?' |       name: | ||||||
|   threepid: |         type: 'localpart' | ||||||
|     type: 'localpart' |         value: 'SELECT idColumn, displayNameColumn FROM table WHERE displayNameColumn LIKE ?' | ||||||
|     value: 'SELECT idColumn, displayNameColumn FROM table WHERE threepidColumn LIKE ?' |       threepid: | ||||||
|  |         type: 'localpart' | ||||||
|  |         value: 'SELECT idColumn, displayNameColumn FROM table WHERE threepidColumn LIKE ?' | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| ### Identity | ### Identity | ||||||
| ```yaml | ```yaml | ||||||
| sql.identity.type: <string> | sql: | ||||||
| sql.identity.query: <string> |   identity: | ||||||
|  |     type: <string> | ||||||
|  |     query: <string> | ||||||
| ``` | ``` | ||||||
|   | |||||||
| @@ -2,26 +2,28 @@ | |||||||
| Synapse's Database itself can be used as an Identity store. | Synapse's Database itself can be used as an Identity store. | ||||||
|  |  | ||||||
| ## Features | ## Features | ||||||
| |      Name      | Supported? | | |                       Name                      | Supported | | ||||||
| |----------------|------------| | |-------------------------------------------------|-----------| | ||||||
| | Authentication | No         | | | [Authentication](../features/authentication.md) | No        | | ||||||
| | Directory      | Yes        | | | [Directory](../features/directory.md)           | Yes       | | ||||||
| | Identity       | Yes        | | | [Identity](../features/identity.md)             | Yes       | | ||||||
| | Profile        | Yes        | | | [Profile](../features/profile.md)               | Yes       | | ||||||
|  |  | ||||||
| Authentication is done by Synapse itself. | Authentication is done by Synapse itself. | ||||||
|  |  | ||||||
| ## Configuration | ## Configuration | ||||||
| ### Basic | ### Basic | ||||||
| ```yaml | ```yaml | ||||||
| synapseSql.enabled: <boolean> | synapseSql: | ||||||
|  |   enabled: <boolean> | ||||||
| ``` | ``` | ||||||
| Enable/disable the identity store | Enable/disable the identity store | ||||||
|  |  | ||||||
| --- | --- | ||||||
|  |  | ||||||
| ```yaml | ```yaml | ||||||
| synapseSql.type: <string> | synapseSql: | ||||||
|  |   type: <string> | ||||||
| ``` | ``` | ||||||
| Set the SQL backend to use which is configured in synapse: | Set the SQL backend to use which is configured in synapse: | ||||||
| - `sqlite` | - `sqlite` | ||||||
| @@ -29,14 +31,16 @@ Set the SQL backend to use which is configured in synapse: | |||||||
|  |  | ||||||
| ### SQLite | ### SQLite | ||||||
| ```yaml | ```yaml | ||||||
| synapseSql.connection: <string> | synapseSql: | ||||||
|  |   connection: <string> | ||||||
| ``` | ``` | ||||||
| Set the value to the absolute path to the Synapse SQLite DB file. | Set the value to the absolute path to the Synapse SQLite DB file. | ||||||
| Example: `/path/to/synapse/sqliteFile.db` | Example: `/path/to/synapse/sqliteFile.db` | ||||||
|  |  | ||||||
| ### PostgreSQL | ### PostgreSQL | ||||||
| ```yaml | ```yaml | ||||||
| synapseSql.connection: //<HOST[:PORT]/DB?user=USER&password=PASS | synapseSql: | ||||||
|  |   connection: //<HOST[:PORT]/DB?user=USER&password=PASS | ||||||
| ``` | ``` | ||||||
| Set the connection info for the database by replacing the following values: | Set the connection info for the database by replacing the following values: | ||||||
| - `HOST`: Hostname of the SQL server | - `HOST`: Hostname of the SQL server | ||||||
|   | |||||||
| @@ -5,12 +5,12 @@ Two types of connections are required for full support: | |||||||
| - Direct SQL access | - Direct SQL access | ||||||
|  |  | ||||||
| ## Features | ## Features | ||||||
| |      Name      | Supported? | | |                       Name                      | Supported | | ||||||
| |----------------|------------| | |-------------------------------------------------|-----------| | ||||||
| | Authentication | Yes        | | | [Authentication](../features/authentication.md) | Yes       | | ||||||
| | Directory      | Yes        | | | [Directory](../features/directory.md)           | Yes       | | ||||||
| | Identity       | Yes        | | | [Identity](../features/identity.md)             | Yes       | | ||||||
| | Profile        | No         | | | [Profile](../features/profile.md)               | No        | | ||||||
|  |  | ||||||
| ## Requirements | ## Requirements | ||||||
| - [Wordpress](https://wordpress.org/download/) >= 4.4 | - [Wordpress](https://wordpress.org/download/) >= 4.4 | ||||||
| @@ -34,22 +34,29 @@ If this is not the case for your installation, the mxisd URL will need to be app | |||||||
| ### mxisd | ### mxisd | ||||||
| Enable in the configuration: | Enable in the configuration: | ||||||
| ```yaml | ```yaml | ||||||
| wordpress.enabled: true | wordpress: | ||||||
|  |   enabled: true | ||||||
| ``` | ``` | ||||||
| Configure the URL to your Wordpress installation - see above about added `/index.php`: | Configure the URL to your Wordpress installation - see above about added `/index.php`: | ||||||
| ```yaml | ```yaml | ||||||
| wordpress.rest.base: 'http://localhost:8080' | wordpress: | ||||||
|  |   rest: | ||||||
|  |     base: 'http://localhost:8080' | ||||||
| ``` | ``` | ||||||
| Configure the SQL connection to your Wordpress database: | Configure the SQL connection to your Wordpress database: | ||||||
| ```yaml | ```yaml | ||||||
| wordpress.sql.connection: '//127.0.0.1/wordpress?user=root&password=example' | wordpress: | ||||||
|  |   sql: | ||||||
|  |     connection: '//127.0.0.1/wordpress?user=root&password=example' | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| --- | --- | ||||||
|  |  | ||||||
| By default, MySQL database is expected. If you use another database, use: | By default, MySQL database is expected. If you use another database, use: | ||||||
| ```yaml | ```yaml | ||||||
| wordpress.sql.type: <string> | wordpress: | ||||||
|  |   sql: | ||||||
|  |     type: <string> | ||||||
| ``` | ``` | ||||||
| With possible values: | With possible values: | ||||||
| - `mysql` | - `mysql` | ||||||
| @@ -61,6 +68,8 @@ With possible values: | |||||||
|  |  | ||||||
| To configure the tables prefix for default queries, in case a custom value was set during Wordpress install: | To configure the tables prefix for default queries, in case a custom value was set during Wordpress install: | ||||||
| ```yaml | ```yaml | ||||||
| wordpress.sql.tablePrefix: <string> | wordpress: | ||||||
|  |   sql: | ||||||
|  |     tablePrefix: <string> | ||||||
| ``` | ``` | ||||||
| By default, the value is set to `wp_`. | By default, the value is set to `wp_`. | ||||||
|   | |||||||
| @@ -5,15 +5,17 @@ Connector ID: `smtp` | |||||||
|  |  | ||||||
| ## Configuration | ## Configuration | ||||||
| ```yaml | ```yaml | ||||||
| threepid.medium.email: | threepid: | ||||||
|   identity: |   medium: | ||||||
|     from: 'identityServerEmail@example.org' |     email: | ||||||
|     name: 'My Identity Server' |       identity: | ||||||
|   connectors: |         from: 'identityServerEmail@example.org' | ||||||
|     smtp: |         name: 'My Identity Server' | ||||||
|       host: 'smtpHostname' |       connectors: | ||||||
|       port: 587 |         smtp: | ||||||
|       tls: 1 # 0 = no STARTLS, 1 = try, 2 = force |           host: 'smtpHostname' | ||||||
|       login: 'smtpLogin' |           port: 587 | ||||||
|       password: 'smtpPassword' |           tls: 1 # 0 = no STARTLS, 1 = try, 2 = force | ||||||
|  |           login: 'smtpLogin' | ||||||
|  |           password: 'smtpPassword' | ||||||
| ``` | ``` | ||||||
|   | |||||||
| @@ -5,7 +5,12 @@ Connector ID: `twilio` | |||||||
|  |  | ||||||
| ## Configuration | ## Configuration | ||||||
| ```yaml | ```yaml | ||||||
| threepid.medium.msisdn.connectors.twilio.accountSid: 'myAccountSid' | threepid: | ||||||
| threepid.medium.msisdn.connectors.twilio.authToken: 'myAuthToken' |   medium: | ||||||
| threepid.medium.msisdn.connectors.twilio.number: '+123456789' |     msisdn: | ||||||
|  |       connectors: | ||||||
|  |         twilio: | ||||||
|  |           accountSid: 'myAccountSid' | ||||||
|  |           authToken: 'myAuthToken' | ||||||
|  |           number: '+123456789' | ||||||
| ``` | ``` | ||||||
|   | |||||||
| @@ -18,20 +18,23 @@ This handler can be used with the 3PID types: | |||||||
| ## Configuration | ## Configuration | ||||||
| Enabled by default or with: | Enabled by default or with: | ||||||
| ```yaml | ```yaml | ||||||
| notification.handler.email: 'raw' | notification: | ||||||
|  |   handler: | ||||||
|  |     email: 'raw' | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| **WARNING:** Will be consolidated soon, prone to breaking changes.   | **WARNING:** Will be consolidated soon, prone to breaking changes.   | ||||||
| Structure and default values: | Structure and default values: | ||||||
| ```yaml | ```yaml | ||||||
| threepid.medium: | threepid: | ||||||
|   email: |   medium: | ||||||
|     identity: |     email: | ||||||
|       from: '' |       identity: | ||||||
|       name: '' |         from: '' | ||||||
|     connector: 'smtp' |         name: '' | ||||||
|     generator: 'template' |       connector: 'smtp' | ||||||
|   msisdn: |       generator: 'template' | ||||||
|     connector: 'twilio' |     msisdn: | ||||||
|     generator: 'template' |       connector: 'twilio' | ||||||
|  |       generator: 'template' | ||||||
| ``` | ``` | ||||||
|   | |||||||
| @@ -1,7 +1,39 @@ | |||||||
| # SendGrid Notification handler | # SendGrid Notification handler | ||||||
| To be completed. See [raw possible configuration items](https://github.com/kamax-matrix/mxisd/blob/master/src/main/resources/application.yaml#L172). | > **WARNING:** This section is incomplete and may be misleading. Contact us if guidance is needed. | ||||||
|  |  | ||||||
| Enabled with: | Enable with: | ||||||
| ```yaml | ```yaml | ||||||
| notification.handler.email: 'sendgrid' | notification: | ||||||
|  |   handler: | ||||||
|  |     email: 'sendgrid' | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | Available Configuration keys: | ||||||
|  | ```yaml | ||||||
|  | notification: | ||||||
|  |   handlers: | ||||||
|  |     sendgrid: | ||||||
|  |       api: | ||||||
|  |         key: <API key> | ||||||
|  |       identity: | ||||||
|  |         from: <Sender email address> | ||||||
|  |         name: <Sender name> | ||||||
|  |       templates: | ||||||
|  |         invite: | ||||||
|  |           subject: <Subject of the email notification sent for room invites> | ||||||
|  |           body: | ||||||
|  |             text: <Path to file containing the raw text part of the email. Do not set to not use one> | ||||||
|  |             html: <Path to file containing the HTML part of the email. Do not set to not use one> | ||||||
|  |         session: | ||||||
|  |           validation: | ||||||
|  |             subject: <Subject of the email notification sent for 3PID sessions> | ||||||
|  |             body: | ||||||
|  |               text: <Path to file containing the raw text part of the email. Do not set to not use one> | ||||||
|  |               html: <Path to file containing the HTML part of the email. Do not set to not use one> | ||||||
|  |           unbind: | ||||||
|  |             fraudulent: | ||||||
|  |               subject: <Subject of the email notification sent for potentially fraudulent 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> | ||||||
| ```  | ```  | ||||||
|   | |||||||
| @@ -11,16 +11,18 @@ placeholders and also have their own individual set of placeholders. | |||||||
| ## Configuration | ## Configuration | ||||||
| To configure paths to the various templates: | To configure paths to the various templates: | ||||||
| ```yaml | ```yaml | ||||||
| threepid.medium.<YOUR 3PID MEDIUM HERE>: | threepid: | ||||||
|   generators: |   medium: | ||||||
|     template: |     <YOUR 3PID MEDIUM HERE>: | ||||||
|       invite: '/path/to/invite-template.eml' |       generators: | ||||||
|       session: |         template: | ||||||
|         validation: |           invite: '/path/to/invite-template.eml' | ||||||
|           local: '/path/to/validate-local-template.eml' |           session: | ||||||
|           remote: 'path/to/validate-remote-template.eml' |             validation: '/path/to/validate-template.eml' | ||||||
|       generic: |             unbind: | ||||||
|         matrixId: '/path/to/mxid-invite-template.eml' |               frandulent: '/path/to/unbind-fraudulent-template.eml' | ||||||
|  |           generic: | ||||||
|  |             matrixId: '/path/to/mxid-invite-template.eml' | ||||||
| ``` | ``` | ||||||
| The `template` generator is usually the default, so no further configuration is needed. | The `template` generator is usually the default, so no further configuration is needed. | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,86 +1,34 @@ | |||||||
| # Web pages for the 3PID sessions | # Web pages for the 3PID sessions | ||||||
| You can customize the various pages used during a 3PID validation using [Thymeleaf templates](http://www.thymeleaf.org/). | You can customize the various pages used during a 3PID validation using the options below. | ||||||
|  |  | ||||||
| ## Configuration | ## Configuration | ||||||
| Pseudo-configuration to illustrate the structure: | Pseudo-configuration to illustrate the structure: | ||||||
| ```yaml | ```yaml | ||||||
| # CONFIGURATION EXAMPLE | # CONFIGURATION EXAMPLE | ||||||
| # DO NOT COPY/PASTE THIS IN YOUR CONFIGURATION | # DO NOT COPY/PASTE THIS IN YOUR CONFIGURATION | ||||||
| view.session.local: | view: | ||||||
|   onTokenSubmit: |   session: | ||||||
|     success: '/path/to/session/local/tokenSubmitSuccess-page.html' |     onTokenSubmit: | ||||||
|     failure: '/path/to/session/local/tokenSubmitFailure-page.html' |       success: '/path/to/session/tokenSubmitSuccess-page.html' | ||||||
| view.session.localRemote: |       failure: '/path/to/session/tokenSubmitFailure-page.html' | ||||||
|   onTokenSubmit: |  | ||||||
|     success: '/path/to/session/localRemote/tokenSubmitSuccess-page.html' |  | ||||||
|     failure: '/path/to/session/local/tokenSubmitFailure-page.html' |  | ||||||
| view.session.remote: |  | ||||||
|   onRequest: |  | ||||||
|     success: '/path/to/session/remote/requestSuccess-page.html' |  | ||||||
|     failure: '/path/to/session/remote/requestFailure-page.html' |  | ||||||
|   onCheck: |  | ||||||
|     success: '/path/to/session/remote/checkSuccess-page.html' |  | ||||||
|     failure: '/path/to/session/remote/checkFailure-page.html' |  | ||||||
| # CONFIGURATION EXAMPLE | # CONFIGURATION EXAMPLE | ||||||
| # DO NOT COPY/PASTE THIS IN YOUR CONFIGURATION | # DO NOT COPY/PASTE THIS IN YOUR CONFIGURATION | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| 3PID session are divided into three config sections: | `view.session`: | ||||||
| - `local` for local-only 3PID sessions |  | ||||||
| - `localRemote` for local 3PID sessions that can also be turned into remote sessions, if the user so desires |  | ||||||
| - `remote` for remote-only 3PID sessions |  | ||||||
|  |  | ||||||
| Each section contains a sub-key per support event. Finally, a `success` and `failure` key is available depending on the |  | ||||||
| outcome of the request. |  | ||||||
|  |  | ||||||
| ## Local |  | ||||||
| ### onTokenSubmit |  | ||||||
| This is triggered when a user submit a validation token for a 3PID session. It is typically visited when clicking the | This is triggered when a user submit a validation token for a 3PID session. It is typically visited when clicking the | ||||||
| link in a validation email. | link in a validation email. | ||||||
|  |  | ||||||
| The template should typically inform the user that the validation was successful and to go back in their Matrix client | The template should typically inform the user that the validation was successful and to go back in their Matrix client | ||||||
| to finish the validation process. | to finish the validation process, or that the validation failed. | ||||||
|  |  | ||||||
| #### Placeholders | Two configuration keys are available that accept paths to HTML templates: | ||||||
|  | - `success` | ||||||
|  | - `failure` | ||||||
|  |  | ||||||
|  | ## Placeholders | ||||||
|  | ### Success | ||||||
| No object/placeholder are currently available. | No object/placeholder are currently available. | ||||||
|  |  | ||||||
| ## Local & Remote | ### Failure | ||||||
| ### onTokenSubmit |  | ||||||
| This is triggered when a user submit a validation token for a 3PID session. It is typically visited when clicking the |  | ||||||
| link in a validation email. |  | ||||||
|  |  | ||||||
| The template should typically inform the user that their 3PID address will not yet be publicly/globally usable. In case |  | ||||||
| they want to make it, they should start a Remote 3PID session with a given link or that they can go back to their Matrix |  | ||||||
| client if they do not wish to proceed any further. |  | ||||||
|  |  | ||||||
| #### Placeholders |  | ||||||
| ##### Success |  | ||||||
| `<a th:href="${remoteSessionLink}">text</a>` can be used to display the link to start a Remote 3PID session. |  | ||||||
|  |  | ||||||
| ##### Failure |  | ||||||
| No object/placeholder are currently available. |  | ||||||
|  |  | ||||||
| ## Remote |  | ||||||
| ### onRequest |  | ||||||
| This is triggered when a user starts a Remote 3PID session, usually from a link produced in the `local.onTokenSubmit` |  | ||||||
| view or in a remote-only 3PID notification. |  | ||||||
|  |  | ||||||
| The template should typically inform the user that the remote creation was successful, followed the instructions sent by |  | ||||||
| the remote Identity server and, once that is done, click a link to validate the session. |  | ||||||
|  |  | ||||||
| #### Placeholders |  | ||||||
| ##### Success |  | ||||||
| `<a th:href="${checkLink}">text</a>` can be used to display the link to validate the Remote 3PID session. |  | ||||||
|  |  | ||||||
| ##### Failure |  | ||||||
| No object/placeholder are currently available. |  | ||||||
|  |  | ||||||
| ### onCheck |  | ||||||
| This is triggered when a user attempts to inform the Identity server that the Remote 3PID session has been validated |  | ||||||
| with the remote Identity server. |  | ||||||
|  |  | ||||||
| The template should typically inform the user that the validation was successful and to go back in their Matrix client |  | ||||||
| to finish the validation process. |  | ||||||
|  |  | ||||||
| #### Placeholders |  | ||||||
| No object/placeholder are currently available. | No object/placeholder are currently available. | ||||||
|   | |||||||
| @@ -1,9 +1,8 @@ | |||||||
| # 3PID Sessions | # 3PID Sessions | ||||||
| - [Overview](#overview) | - [Overview](#overview) | ||||||
| - [Purpose](#purpose) | - [Restrictions](#restrictions) | ||||||
| - [Federation](#federation) |   - [Bindings](#bindings) | ||||||
|   - [3PID scope](#3pid-scope) |   - [Federation](#federation) | ||||||
|   - [Session scope](#session-scope) |  | ||||||
| - [Notifications](#notifications) | - [Notifications](#notifications) | ||||||
|   - [Email](#email) |   - [Email](#email) | ||||||
|   - [Phone numbers](#msisdn-(phone-numbers)) |   - [Phone numbers](#msisdn-(phone-numbers)) | ||||||
| @@ -11,28 +10,39 @@ | |||||||
|   - [Configuration](#configuration) |   - [Configuration](#configuration) | ||||||
|   - [Web views](#web-views) |   - [Web views](#web-views) | ||||||
|   - [Scenarios](#scenarios) |   - [Scenarios](#scenarios) | ||||||
|     - [Default](#default) |  | ||||||
|     - [Local sessions only](#local-sessions-only) |  | ||||||
|     - [Remote sessions only](#remote-sessions-only) |  | ||||||
|     - [Sessions disabled](#sessions-disabled) |     - [Sessions disabled](#sessions-disabled) | ||||||
|  |  | ||||||
| ## Overview | ## Overview | ||||||
| When adding an email, a phone number or any other kind of 3PID (Third-Party Identifier) in a Matrix client, | When adding an email, a phone number or any other kind of 3PID (Third-Party Identifier) in a Matrix client, | ||||||
| the identity server is contacted to validate the 3PID. | the identity server is contacted to validate the 3PID. | ||||||
|  |  | ||||||
| To validate the 3PID the identity server sends a message to the 3PID (e.g. an | To validate the 3PID, the identity server creates a session associated with a secret token. That token is sent via a message | ||||||
| email) with a hyperlink back to a web-page managed by the identity server to | to the 3PID (e.g. an email) with a the necessary info so the user can submit them to the Identity Server, confirm ownership | ||||||
| confirm ownership of the 3PID. | of the 3PID. | ||||||
|  |  | ||||||
| Once this 3PID is validated, the Homeserver will publish the user Matrix ID on the Identity Server and | Once this 3PID is validated, the Homeserver will request that the Identity Server links the provided user Matrix ID with | ||||||
| add this 3PID to the Matrix account which initiated the request. | the 3PID session and finally add the 3PID to its own data store. | ||||||
|  |  | ||||||
| This serves two purposes: | This serves two purposes: | ||||||
| - Add the 3PID as an administrative/login info for the Homeserver directly | - Add the 3PID as an administrative/login info for the Homeserver directly | ||||||
| - Publish, or *Bind*, the 3PID so it can be queried from Homeservers and clients when inviting someone in a room | - Links, called *Bind*, the 3PID so it can be queried from Homeservers and clients when inviting someone in a room | ||||||
| by a 3PID, allowing it to be resolved to a Matrix ID. | by a 3PID, allowing it to be resolved to a Matrix ID. | ||||||
|  |  | ||||||
| ## Federation | ## Restrictions | ||||||
|  | ### Bindings | ||||||
|  | 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/saved anywhere or can be used to invite/search the user. | ||||||
|  |  | ||||||
|  | Identity stores are the ones holding such data, irrelevant if a user added a 3PID to their profile. When queried for | ||||||
|  | bindings, mxisd will query Identity stores which are responsible to store this kind of information. | ||||||
|  |  | ||||||
|  | Therefore, by default, any 3PID added to a user profile which is NOT within a configured and enabled Identity backend | ||||||
|  | will simply not be usable for search or invites, **even on the same Homeserver!**   | ||||||
|  |  | ||||||
|  | To have such 3PID bindings available for search and invite queries on synapse, use its dedicated | ||||||
|  | [Identity store](../../stores/synapse.md). | ||||||
|  |  | ||||||
|  | ### Federation | ||||||
| In a federated set up, identity servers must cooperate to find the Matrix ID associated with a 3PID. | In a federated set up, identity servers must cooperate to find the Matrix ID associated with a 3PID. | ||||||
|  |  | ||||||
| Federation is based on the principle that each server is responsible for its own (dns) domain. | Federation is based on the principle that each server is responsible for its own (dns) domain. | ||||||
| @@ -43,61 +53,15 @@ Example: a user from Homeserver `example.org` adds an email `john@example.com`. | |||||||
| Federated identity servers would try to find the identity server at `example.com` and ask it for the Matrix ID of associated with `john@example.com`. | Federated identity servers would try to find the identity server at `example.com` and ask it for the Matrix ID of associated with `john@example.com`. | ||||||
|  |  | ||||||
| Nevertheless, Matrix users might add 3PIDs that are not associated to a domain, for example telephone numbers. | Nevertheless, Matrix users might add 3PIDs that are not associated to a domain, for example telephone numbers. | ||||||
| Or they might even add 3PIDs associated to a different domain (such as an email address hosted by gmail). | Or they might even add 3PIDs associated to a different domain (such as an email address hosted by Gmail). | ||||||
| Such 3PIDs cannot be resolved in a federated way. | Such 3PIDs cannot be resolved in a federated way and will not be found from other servers. | ||||||
|  |  | ||||||
| Example: a user from Homeserver `example.org` adds an email `john@gmail.com`.   | Example: a user from Homeserver `example.org` adds an email `john@gmail.com`.   | ||||||
| If a federated lookup was performed, Identity servers would try to find the 3PID bind at the `gmail.com` server, and | If a federated lookup was performed, Identity servers would try to find the 3PID bind at the `gmail.com` server, and | ||||||
| not `example.org`. | not `example.org`. | ||||||
|  |  | ||||||
| In order to resolve such 3PIDs, i.e. 3PIDs that cannot be resolved in a Federated way, an identity server can be configured such that | As mxisd is built for self-hosted use cases, mainly for orgs/corps, this is usually not a problem for emails.   | ||||||
| - 3PIDs that cannot be resolved locally or using federation, are fowarded to another global identity server. | Sadly, there is currently no mechanism to make this work for phone numbers.  | ||||||
| - registration of new 3PIDs that cannot be looked up in a federated fashion, is forwarded to another global identity server. |  | ||||||
|  |  | ||||||
| By forwarding a 3PIDs registration the identity creates a *Remote session* and *Remote bind*, effectively starting a new 3PID session with another Identity server on |  | ||||||
| behalf of the user.  |  | ||||||
|  |  | ||||||
| To ensure lookup works consistency within the current Matrix network, the central Matrix.org Identity Server should be |  | ||||||
| used to store *remote* sessions and binds. |  | ||||||
|  |  | ||||||
| However, at the time of writing, the Matrix specification and the central Matrix.org servers do not allow to remote a 3PID bind. |  | ||||||
| This means that once a 3PID is published (email, phone number, etc.), it cannot be easily removed |  | ||||||
| and would require contacting the Matrix.org administrators for each bind individually. |  | ||||||
| This poses a privacy, control and security concern, especially for groups/corporations that want to keep a tight control |  | ||||||
| on where such identifiers can be made publicly visible. |  | ||||||
|  |  | ||||||
| To ensure full control, validation management relies on two concepts: |  | ||||||
| - The scope of 3PID being validated |  | ||||||
| - The scope of 3PID sessions that should be possible/offered |  | ||||||
|  |  | ||||||
| ### 3PID scope |  | ||||||
| 3PID can either be scoped as local or remote. |  | ||||||
|  |  | ||||||
| Local means that they can be looked up using federation and that such a federation call would end up on the local |  | ||||||
| Identity Server.   |  | ||||||
| Remote means that they cannot be lookup using federation or that a federation call would not end up on the local |  | ||||||
| Identity Server. |  | ||||||
|  |  | ||||||
| Email addresses can either be local or remote 3PID, depending on the domain. If the address is one from the configured |  | ||||||
| domain in the Identity server, it will be scoped as local. If it is from another domain, it will be as remote. |  | ||||||
|  |  | ||||||
| Phone number can only be scoped as remote, since there is currently no way to perform DNS queries that would lead back |  | ||||||
| to the Identity server who validated the phone number. |  | ||||||
|  |  | ||||||
| ### Session scope |  | ||||||
| Sessions can be scoped as: |  | ||||||
| - Local only - validate 3PIDs directly, do not allow the creation of 3PID sessions on a remote Identity server. |  | ||||||
| - Local and Remote - validate 3PIDs directly, offer users to option to also validate and bind 3PID on another server. |  | ||||||
| - Remote only - validate and bind 3PIDs on another server, no validation or bind done locally. |  | ||||||
|  |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| **IMPORTANT NOTE:** mxisd does not store bindings directly. While a user can see its email, phone number or any other |  | ||||||
| 3PID in its settings/profile, it does **NOT** mean it is published anywhere and can be used to invite/search the user. |  | ||||||
| Identity stores are the ones holding such data.   |  | ||||||
| If you still want added arbitrary 3PIDs to be discoverable on a synapse Homeserver, use the corresponding [Identity store](../../stores/synapse.md). |  | ||||||
|  |  | ||||||
| See the [Scenarios](#scenarios) for more info on how and why. |  | ||||||
|  |  | ||||||
| ## Notifications | ## Notifications | ||||||
| 3PIDs are validated by sending a pre-formatted message containing a token to that 3PID address, which must be given to the | 3PIDs are validated by sending a pre-formatted message containing a token to that 3PID address, which must be given to the | ||||||
| @@ -126,44 +90,36 @@ Connectors: | |||||||
|  |  | ||||||
| ## Usage | ## Usage | ||||||
| ### Configuration | ### Configuration | ||||||
| The following example of configuration (incomplete extract) shows which items are relevant for 3PID sessions. | The following example of configuration shows which items are relevant for 3PID sessions. | ||||||
|  |  | ||||||
| **IMPORTANT:** Most configuration items shown have default values and should not be included in your own configuration | **IMPORTANT:** Most configuration items shown have default values and should not be included in your own configuration | ||||||
| file unless you want to specifically overwrite them. | file unless you want to specifically overwrite them. | ||||||
| ```yaml | ```yaml | ||||||
| # CONFIGURATION EXAMPLE | # CONFIGURATION EXAMPLE | ||||||
| # DO NOT COPY/PASTE THIS IN YOUR CONFIGURATION | # DO NOT COPY/PASTE AS-IS IN YOUR CONFIGURATION | ||||||
| session.policy.validation.enabled: true |  | ||||||
| session.policy.validation.forLocal: | session: | ||||||
|   enabled: true |   policy: | ||||||
|   toLocal: true |     validation: | ||||||
|   toRemote: |       enabled: true | ||||||
|     enabled: true |     unbind: | ||||||
|     server: 'configExample'  # Not to be included in config! Already present in default config! |       fraudulent: | ||||||
| session.policy.validation.forRemote: |         sendWarning: true | ||||||
|   enabled: true |  | ||||||
|   toLocal: true | # DO NOT COPY/PASTE AS-IS IN YOUR CONFIGURATION | ||||||
|   toRemote: |  | ||||||
|     enabled: true |  | ||||||
|     server: 'configExample'  # Not to be included in config! Already present in default config! |  | ||||||
| # DO NOT COPY/PASTE THIS IN YOUR CONFIGURATION |  | ||||||
| # CONFIGURATION EXAMPLE | # CONFIGURATION EXAMPLE | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| `session.policy.validation` is the core configuration to control what users configured to use your Identity server | `session.policy.validation` is the core configuration to control what users configured to use your Identity server | ||||||
| are allowed to do in terms of 3PID sessions. | are allowed to do in terms of 3PID sessions. The policy has a global on/off switch for 3PID sessions using `.enabled`   | ||||||
|  |  | ||||||
| The policy has a global on/off switch for 3PID sessions using `.enabled`   | --- | ||||||
| It is also divided into two sections: `forLocal` and `forRemote` which refers to the 3PID scopes.   |  | ||||||
|  |  | ||||||
| Each scope is divided into three parts: | `unbind.fraudulent` controls warning notifications if an illegal/fraudulent 3PID removal is attempted on the Identity server.   | ||||||
| - global on/off switch for 3PID sessions using `.enabled` | This is directly related to synapse disregard for privacy and new GDPR laws in Europe in an attempt to inform users about | ||||||
| - `toLocal` allowing or not local 3PID session validations | potential privacy leaks. | ||||||
| - `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 | For more information, see the corresponding [synapse issue](https://github.com/matrix-org/synapse/issues/4540). | ||||||
| locally validated. |  | ||||||
|  |  | ||||||
| ### Web views | ### Web views | ||||||
| Once a user click on a validation link, it is taken to the Identity Server validation page where the token is submitted.   | Once a user click on a validation link, it is taken to the Identity Server validation page where the token is submitted.   | ||||||
| @@ -174,103 +130,18 @@ See [the dedicated document](session-views.md) | |||||||
| on how to configure/customize/brand those pages to your liking. | on how to configure/customize/brand those pages to your liking. | ||||||
|  |  | ||||||
| ### Scenarios | ### Scenarios | ||||||
| It is important to keep in mind that mxisd does not create bindings, irrelevant if a user added a 3PID to their profile.   |  | ||||||
| Instead, when queried for bindings, mxisd will query Identity stores which are responsible to store this kind of information. |  | ||||||
|  |  | ||||||
| This has the side effect that any 3PID added to a user profile which is NOT within a configured and enabled Identity backend |  | ||||||
| will simply not be usable for search or invites, **even on the same Homeserver!**   |  | ||||||
| mxisd does not store binds on purpose, as one of its primary goal is to ensure maximum compatibility with federation |  | ||||||
| and the rest of the Matrix ecosystem is preserved. |  | ||||||
|  |  | ||||||
| Nonetheless, because mxisd also aims at offering support for tight control over identity data, it is possible to have |  | ||||||
| such 3PID bindings available for search and invite queries on synapse with the corresponding [Identity store](../../stores/synapse.md). |  | ||||||
|  |  | ||||||
| See the [Local sessions only](#local-sessions-only) use case for more information on how to configure. |  | ||||||
|  |  | ||||||
| #### Default |  | ||||||
| By default, mxisd allows the following: |  | ||||||
|  |  | ||||||
| |                 | Local Session     | Remote Session | |  | ||||||
| |-----------------|-------------------|----------------| |  | ||||||
| | **Local 3PID**  | Yes               | Yes, offered   | |  | ||||||
| | **Remote 3PID** | No, Remote forced | Yes            | |  | ||||||
|  |  | ||||||
| This is usually what people expect and will feel natural to users and does not involve further integration. |  | ||||||
|  |  | ||||||
| This allows to stay in control for e-mail addresses which domain matches your Matrix environment, still making them |  | ||||||
| discoverable with federation but not recorded in a 3rd party Identity server which is not under your control.   |  | ||||||
| Users still get the possibility to publish globally their address if needed. |  | ||||||
|  |  | ||||||
| Other e-mail addresses and phone number will be redirected to remote sessions to ensure full compatibility with the Matrix |  | ||||||
| ecosystem and other federated servers. |  | ||||||
|  |  | ||||||
| #### Local sessions only |  | ||||||
| **NOTE:** This does not affect 3PID lookups (queries to find Matrix IDs). See [Federation](../../features/federation.md) |  | ||||||
| to disable remote lookup for those. |  | ||||||
|  |  | ||||||
| This configuration ensures maximum confidentiality and privacy. |  | ||||||
| Typical use cases: |  | ||||||
| - Private Homeserver, not federated |  | ||||||
| - Internal Homeserver without direct Internet access |  | ||||||
| - Custom product based on Matrix which does not federate |  | ||||||
|  |  | ||||||
| No 3PID will be sent to a remote Identity server and all validation will be performed locally.   |  | ||||||
| On the flip side, people with *Remote* 3PID scopes will not be found from other servers. |  | ||||||
|  |  | ||||||
| Use the following values: |  | ||||||
| ```yaml |  | ||||||
| session.policy.validation.enabled: true |  | ||||||
| session.policy.validation.forLocal: |  | ||||||
|   enabled: true |  | ||||||
|   toLocal: true |  | ||||||
|   toRemote: |  | ||||||
|     enabled: false |  | ||||||
| session.policy.validation.forRemote: |  | ||||||
|   enabled: true |  | ||||||
|   toLocal: true |  | ||||||
|   toRemote: |  | ||||||
|     enabled: false |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| **IMPORTANT**: When using local-only mode and if you are using synapse, you will also need to enable its dedicated Identity |  | ||||||
| store if you want user searches and invites to work. To do so, see the [dedicated document](../../stores/synapse.md). |  | ||||||
|  |  | ||||||
| #### Remote sessions only |  | ||||||
| This configuration ensures all 3PID are made public for maximum compatibility and reach within the Matrix ecosystem, at |  | ||||||
| the cost of confidentiality and privacy.   |  | ||||||
|  |  | ||||||
| Typical use cases: |  | ||||||
| - Public Homeserver |  | ||||||
| - Homeserver with registration enabled |  | ||||||
|  |  | ||||||
| Use the following values: |  | ||||||
| ```yaml |  | ||||||
| session.policy.validation.enabled: true |  | ||||||
| session.policy.validation.forLocal: |  | ||||||
|   enabled: true |  | ||||||
|   toLocal: false |  | ||||||
|   toRemote: |  | ||||||
|     enabled: true |  | ||||||
| session.policy.validation.forRemote: |  | ||||||
|   enabled: true |  | ||||||
|   toLocal: false |  | ||||||
|   toRemote: |  | ||||||
|     enabled: true |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| #### Sessions disabled | #### Sessions disabled | ||||||
| This configuration would disable 3PID session altogether, preventing users from adding emails and/or phone numbers to | This configuration would disable 3PID sessions altogether, preventing users from validating emails and/or phone numbers | ||||||
| their profiles.   | and any subsequent actions that requires them, like adding them to their profiles. | ||||||
|  |    | ||||||
| This would be used if mxisd is also performing authentication for the Homeserver, typically with synapse and the | 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). | [REST password provider](https://github.com/kamax-matrix/matrix-synapse-rest-auth), where 3PID mappings would be | ||||||
|  | auto-populated. | ||||||
| **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. |  | ||||||
|  |  | ||||||
| Use the following values to enable this mode: | Use the following values to enable this mode: | ||||||
| ```yaml | ```yaml | ||||||
| session.policy.validation.enabled: false | session: | ||||||
|  |   policy: | ||||||
|  |     validation: | ||||||
|  |       enabled: false | ||||||
| ``` | ``` | ||||||
|   | |||||||
| @@ -1,50 +1,46 @@ | |||||||
| # Sample configuration file explaining the minimum required keys to be set to run mxisd | # Sample configuration file explaining the minimum required keys to be set to run mxisd | ||||||
| # | # | ||||||
| # For a complete list of options, see https://github.com/kamax-matrix/mxisd | # For a complete list of options, see https://github.com/kamax-matrix/mxisd/docs/README.md | ||||||
| 
 | 
 | ||||||
| ####################### | ####################### | ||||||
| # Matrix config items # | # Matrix config items # | ||||||
| ####################### | ####################### | ||||||
| # Matrix domain, same as the domain configure in your Homeserver configuration. | # 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). | # NOTE: in Synapse Homeserver, the Matrix domain is defined as 'server_name' in configuration file. | ||||||
| # | # | ||||||
| # This is used to build the various identifiers for identity, auth and directory. | # This is used to build the various identifiers in all the features. | ||||||
| matrix.domain: '' | matrix: | ||||||
|  |   domain: '' | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| ################ | ################ | ||||||
| # Signing keys # | # Signing keys # | ||||||
| ################ | ################ | ||||||
| # Absolute path for the Identity Server signing key. | # Absolute path for the Identity Server signing key. | ||||||
| # During testing, /var/tmp/mxisd.key is a possible value | # This is **NOT** your homeserver key. | ||||||
|  | # The signing key is auto-generated during execution time if not present. | ||||||
| # | # | ||||||
|  | # During testing, /var/tmp/mxisd.key is a possible value | ||||||
| # For production, recommended location shall be one of the following: | # For production, recommended location shall be one of the following: | ||||||
| #   - /var/opt/mxisd/sign.key | #   - /var/opt/mxisd/sign.key | ||||||
| #   - /var/local/mxisd/sign.key | #   - /var/local/mxisd/sign.key | ||||||
| #   - /var/lib/mxisd/sign.key | #   - /var/lib/mxisd/sign.key | ||||||
| # | # | ||||||
| # The signing key is auto-generated during execution time if not present. | key: | ||||||
| key.path: '' |   path: '' | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| ############################ | # Path to the SQLite DB file for mxisd internal storage | ||||||
| # 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: | # Examples: | ||||||
| #  - /var/opt/mxisd/mxisd.db | #  - /var/opt/mxisd/mxisd.db | ||||||
| #  - /var/local/mxisd/mxisd.db | #  - /var/local/mxisd/mxisd.db | ||||||
| #  - /var/lib/mxisd/mxisd.db | #  - /var/lib/mxisd/mxisd.db | ||||||
| # | # | ||||||
| storage.provider.sqlite.database: '/path/to/mxisd.db' | storage: | ||||||
|  |   provider: | ||||||
|  |     sqlite: | ||||||
|  |       database: '/path/to/mxisd.db' | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| #################### | #################### | ||||||
| @@ -52,12 +48,13 @@ storage.provider.sqlite.database: '/path/to/mxisd.db' | |||||||
| #################### | #################### | ||||||
| # | # | ||||||
| # Root/Central servers to be used as final fallback when performing lookups. | # Root/Central servers to be used as final fallback when performing lookups. | ||||||
| # By default, for privacy reasons, matrix.org servers are not enabled anymore. | # By default, for privacy reasons, matrix.org servers are not enabled. | ||||||
| # See the following issue: https://github.com/kamax-matrix/mxisd/issues/76 | # See the following issue: https://github.com/kamax-matrix/mxisd/issues/76 | ||||||
| # | # | ||||||
| # If you would like to use them and trade away your privacy for convenience, uncomment the following option: | # If you would like to use them and trade away your privacy for convenience, uncomment the following option: | ||||||
| # | # | ||||||
| #forward.servers: ['matrix-org'] | #forward: | ||||||
|  | #  servers: ['matrix-org'] | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| ################ | ################ | ||||||
| @@ -88,27 +85,32 @@ storage.provider.sqlite.database: '/path/to/mxisd.db' | |||||||
| # see https://github.com/kamax-matrix/mxisd/blob/master/docs/threepids/notification/template-generator.md | # see https://github.com/kamax-matrix/mxisd/blob/master/docs/threepids/notification/template-generator.md | ||||||
| # | # | ||||||
| #### E-mail invite sender | #### E-mail invite sender | ||||||
| # | threepid: | ||||||
| # SMTP host |   medium: | ||||||
| threepid.medium.email.connectors.smtp.host: "smtp.example.org" |     email: | ||||||
|  |       identity: | ||||||
|  |         # The e-mail to send as. | ||||||
|  |         from: "matrix-identity@example.org" | ||||||
| 
 | 
 | ||||||
| # SMTP port |       connectors: | ||||||
| threepid.medium.email.connectors.smtp.port: 587 |         smtp: | ||||||
|  |           # SMTP host | ||||||
|  |           host: "smtp.example.org" | ||||||
| 
 | 
 | ||||||
| # TLS mode for the connection. |           # SMTP port | ||||||
| # |           port: 587 | ||||||
| # 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 |           # TLS mode for the connection. | ||||||
| threepid.medium.email.connectors.smtp.login: "matrix-identity@example.org" |           # | ||||||
|  |           # Possible values: | ||||||
|  |           #  0    Disable TLS entirely | ||||||
|  |           #  1    Enable TLS if supported by server (default) | ||||||
|  |           #  2    Force TLS and fail if not available | ||||||
|  |           # | ||||||
|  |           tls: 1 | ||||||
| 
 | 
 | ||||||
| # Password for the account |           # Login for SMTP | ||||||
| threepid.medium.email.connectors.smtp.password: "ThePassword" |           login: "matrix-identity@example.org" | ||||||
| 
 | 
 | ||||||
| # The e-mail to send as. |           # Password for the account | ||||||
| threepid.medium.email.identity.from: "matrix-identity@example.org" |           password: "ThePassword" | ||||||
| @@ -6,8 +6,8 @@ useradd -r mxisd || true | |||||||
| # Set permissions for data directory | # Set permissions for data directory | ||||||
| chown -R mxisd:mxisd %DEB_DATA_DIR% | chown -R mxisd:mxisd %DEB_DATA_DIR% | ||||||
|  |  | ||||||
| # Create symlink to mxusd | # Create symlink to mxisd run script | ||||||
| ln -sfT /usr/lib/mxisd/mxisd.jar /usr/bin/mxisd | ln -sfT /usr/lib/mxisd/mxisd /usr/bin/mxisd | ||||||
|  |  | ||||||
| # Enable systemd service | # Enable systemd service | ||||||
| systemctl enable mxisd.service | systemctl enable mxisd.service | ||||||
|   | |||||||
| @@ -1,25 +1,34 @@ | |||||||
| #!/usr/bin/env bash | #!/bin/bash | ||||||
|  |  | ||||||
| if [[ -n "$CONF_FILE_PATH" ]] && [ ! -f "$CONF_FILE_PATH" ]; then | if [[ -n "$CONF_FILE_PATH" ]] && [ ! -f "$CONF_FILE_PATH" ]; then | ||||||
|     echo "Generating config file $CONF_FILE_PATH" |     echo "Generating config file $CONF_FILE_PATH" | ||||||
|     touch "CONF_FILE_PATH" |     touch "CONF_FILE_PATH" | ||||||
|  |  | ||||||
|     if [[ -n "$MATRIX_DOMAIN" ]]; then |     if [[ -n "$MATRIX_DOMAIN" ]]; then | ||||||
|         echo "Setting matrix domain to $MATRIX_DOMAIN" |         echo "Setting matrix domain to $MATRIX_DOMAIN" | ||||||
|         echo "matrix.domain: $MATRIX_DOMAIN" >> "$CONF_FILE_PATH" |         echo "matrix:" >> "$CONF_FILE_PATH" | ||||||
|  |         echo "  domain: '$MATRIX_DOMAIN'" >> "$CONF_FILE_PATH" | ||||||
|  |         echo >> "$CONF_FILE_PATH" | ||||||
|     fi |     fi | ||||||
|  |  | ||||||
|     if [[ -n "$SIGN_KEY_PATH" ]]; then |     if [[ -n "$SIGN_KEY_PATH" ]]; then | ||||||
|         echo "Setting signing key path to $SIGN_KEY_PATH" |         echo "Setting signing key path to $SIGN_KEY_PATH" | ||||||
|         echo "key.path: $SIGN_KEY_PATH" >> "$CONF_FILE_PATH" |         echo "key:" >> "$CONF_FILE_PATH" | ||||||
|  |         echo "  path: '$SIGN_KEY_PATH'" >> "$CONF_FILE_PATH" | ||||||
|  |         echo >> "$CONF_FILE_PATH" | ||||||
|     fi |     fi | ||||||
|  |  | ||||||
|     if [[ -n "$SQLITE_DATABASE_PATH" ]]; then |     if [[ -n "$SQLITE_DATABASE_PATH" ]]; then | ||||||
|         echo "Setting SQLite DB path to $SQLITE_DATABASE_PATH" |         echo "Setting SQLite DB path to $SQLITE_DATABASE_PATH" | ||||||
|         echo "storage.provider.sqlite.database: $SQLITE_DATABASE_PATH" >> "$CONF_FILE_PATH" |         echo "storage:" >> "$CONF_FILE_PATH" | ||||||
|  |         echo "  provider:" >> "$CONF_FILE_PATH" | ||||||
|  |         echo "    sqlite:" >> "$CONF_FILE_PATH" | ||||||
|  |         echo "      database: '$SQLITE_DATABASE_PATH'" >> "$CONF_FILE_PATH" | ||||||
|  |         echo >> "$CONF_FILE_PATH" | ||||||
|     fi |     fi | ||||||
|  |  | ||||||
|     echo "Starting mxisd..." |     echo "Starting mxisd..." | ||||||
|     echo |     echo | ||||||
| fi | fi | ||||||
|  |  | ||||||
| exec java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -Dspring.config.location=/etc/mxisd/ -Dspring.config.name=mxisd -jar /mxisd.jar | exec java -jar /app/mxisd.jar -c /etc/mxisd/mxisd.yaml | ||||||
|   | |||||||
| @@ -37,6 +37,7 @@ import java.util.List; | |||||||
|  * both IPv4 and IPv6. |  * both IPv4 and IPv6. | ||||||
|  */ |  */ | ||||||
| public class CIDRUtils { | public class CIDRUtils { | ||||||
|  |  | ||||||
|     private final String cidr; |     private final String cidr; | ||||||
|  |  | ||||||
|     private InetAddress inetAddress; |     private InetAddress inetAddress; | ||||||
| @@ -44,7 +45,6 @@ public class CIDRUtils { | |||||||
|     private InetAddress endAddress; |     private InetAddress endAddress; | ||||||
|     private final int prefixLength; |     private final int prefixLength; | ||||||
|  |  | ||||||
|  |  | ||||||
|     public CIDRUtils(String cidr) throws UnknownHostException { |     public CIDRUtils(String cidr) throws UnknownHostException { | ||||||
|  |  | ||||||
|         this.cidr = cidr; |         this.cidr = cidr; | ||||||
| @@ -66,7 +66,6 @@ public class CIDRUtils { | |||||||
|  |  | ||||||
|  |  | ||||||
|     private void calculate() throws UnknownHostException { |     private void calculate() throws UnknownHostException { | ||||||
|  |  | ||||||
|         ByteBuffer maskBuffer; |         ByteBuffer maskBuffer; | ||||||
|         int targetSize; |         int targetSize; | ||||||
|         if (inetAddress.getAddress().length == 4) { |         if (inetAddress.getAddress().length == 4) { | ||||||
| @@ -120,14 +119,9 @@ public class CIDRUtils { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     public String getNetworkAddress() { |     public String getNetworkAddress() { | ||||||
|  |  | ||||||
|         return this.startAddress.getHostAddress(); |         return this.startAddress.getHostAddress(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public String getBroadcastAddress() { |  | ||||||
|         return this.endAddress.getHostAddress(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public boolean isInRange(String ipAddress) throws UnknownHostException { |     public boolean isInRange(String ipAddress) throws UnknownHostException { | ||||||
|         InetAddress address = InetAddress.getByName(ipAddress); |         InetAddress address = InetAddress.getByName(ipAddress); | ||||||
|         BigInteger start = new BigInteger(1, this.startAddress.getAddress()); |         BigInteger start = new BigInteger(1, this.startAddress.getAddress()); | ||||||
| @@ -139,4 +133,5 @@ public class CIDRUtils { | |||||||
|  |  | ||||||
|         return (st == -1 || st == 0) && (te == -1 || te == 0); |         return (st == -1 || st == 0) && (te == -1 || te == 0); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										117
									
								
								src/main/java/io/kamax/mxisd/HttpMxisd.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								src/main/java/io/kamax/mxisd/HttpMxisd.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,117 @@ | |||||||
|  | /* | ||||||
|  |  * mxisd - Matrix Identity Server Daemon | ||||||
|  |  * 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.mxisd; | ||||||
|  |  | ||||||
|  | import io.kamax.mxisd.config.MxisdConfig; | ||||||
|  | import io.kamax.mxisd.http.undertow.handler.OptionsHandler; | ||||||
|  | import io.kamax.mxisd.http.undertow.handler.SaneHandler; | ||||||
|  | import io.kamax.mxisd.http.undertow.handler.as.v1.AsNotFoundHandler; | ||||||
|  | import io.kamax.mxisd.http.undertow.handler.as.v1.AsTransactionHandler; | ||||||
|  | import io.kamax.mxisd.http.undertow.handler.auth.RestAuthHandler; | ||||||
|  | import io.kamax.mxisd.http.undertow.handler.auth.v1.LoginGetHandler; | ||||||
|  | import io.kamax.mxisd.http.undertow.handler.auth.v1.LoginHandler; | ||||||
|  | import io.kamax.mxisd.http.undertow.handler.auth.v1.LoginPostHandler; | ||||||
|  | import io.kamax.mxisd.http.undertow.handler.directory.v1.UserDirectorySearchHandler; | ||||||
|  | import io.kamax.mxisd.http.undertow.handler.identity.v1.*; | ||||||
|  | import io.kamax.mxisd.http.undertow.handler.profile.v1.InternalProfileHandler; | ||||||
|  | import io.kamax.mxisd.http.undertow.handler.profile.v1.ProfileHandler; | ||||||
|  | import io.kamax.mxisd.http.undertow.handler.status.StatusHandler; | ||||||
|  | import io.undertow.Handlers; | ||||||
|  | import io.undertow.Undertow; | ||||||
|  | import io.undertow.server.HttpHandler; | ||||||
|  |  | ||||||
|  | public class HttpMxisd { | ||||||
|  |  | ||||||
|  |     // Core | ||||||
|  |     private Mxisd m; | ||||||
|  |  | ||||||
|  |     // I/O | ||||||
|  |     private Undertow httpSrv; | ||||||
|  |  | ||||||
|  |     public HttpMxisd(MxisdConfig cfg) { | ||||||
|  |         m = new Mxisd(cfg); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void start() { | ||||||
|  |         m.start(); | ||||||
|  |  | ||||||
|  |         HttpHandler helloHandler = SaneHandler.around(new HelloHandler()); | ||||||
|  |         HttpHandler asNotFoundHandler = SaneHandler.around(new AsNotFoundHandler(m.getAs())); | ||||||
|  |         HttpHandler asTxnHandler = SaneHandler.around(new AsTransactionHandler(m.getAs())); | ||||||
|  |         HttpHandler storeInvHandler = SaneHandler.around(new StoreInviteHandler(m.getConfig().getServer(), m.getInvitationManager(), m.getKeyManager())); | ||||||
|  |         HttpHandler sessValidateHandler = SaneHandler.around(new SessionValidateHandler(m.getSession(), m.getConfig().getServer(), m.getConfig().getView())); | ||||||
|  |  | ||||||
|  |         httpSrv = Undertow.builder().addHttpListener(m.getConfig().getServer().getPort(), "0.0.0.0").setHandler(Handlers.routing() | ||||||
|  |  | ||||||
|  |                 .add("OPTIONS", "/**", SaneHandler.around(new OptionsHandler())) | ||||||
|  |  | ||||||
|  |                 // Status endpoints | ||||||
|  |                 .get(StatusHandler.Path, SaneHandler.around(new StatusHandler())) | ||||||
|  |  | ||||||
|  |                 // Authentication endpoints | ||||||
|  |                 .get(LoginHandler.Path, SaneHandler.around(new LoginGetHandler(m.getAuth(), m.getHttpClient()))) | ||||||
|  |                 .post(LoginHandler.Path, SaneHandler.around(new LoginPostHandler(m.getAuth()))) | ||||||
|  |                 .post(RestAuthHandler.Path, SaneHandler.around(new RestAuthHandler(m.getAuth()))) | ||||||
|  |  | ||||||
|  |                 // Directory endpoints | ||||||
|  |                 .post(UserDirectorySearchHandler.Path, SaneHandler.around(new UserDirectorySearchHandler(m.getDirectory()))) | ||||||
|  |  | ||||||
|  |                 // Key endpoints | ||||||
|  |                 .get(KeyGetHandler.Path, SaneHandler.around(new KeyGetHandler(m.getKeyManager()))) | ||||||
|  |                 .get(RegularKeyIsValidHandler.Path, SaneHandler.around(new RegularKeyIsValidHandler(m.getKeyManager()))) | ||||||
|  |                 .get(EphemeralKeyIsValidHandler.Path, SaneHandler.around(new EphemeralKeyIsValidHandler())) | ||||||
|  |  | ||||||
|  |                 // Identity endpoints | ||||||
|  |                 .get(HelloHandler.Path, helloHandler) | ||||||
|  |                 .get(HelloHandler.Path + "/", helloHandler) // Be lax with possibly trailing slash | ||||||
|  |                 .get(SingleLookupHandler.Path, SaneHandler.around(new SingleLookupHandler(m.getIdentity(), m.getSign()))) | ||||||
|  |                 .post(BulkLookupHandler.Path, SaneHandler.around(new BulkLookupHandler(m.getIdentity()))) | ||||||
|  |                 .post(StoreInviteHandler.Path, storeInvHandler) | ||||||
|  |                 .post(SessionStartHandler.Path, SaneHandler.around(new SessionStartHandler(m.getSession()))) | ||||||
|  |                 .get(SessionValidateHandler.Path, sessValidateHandler) | ||||||
|  |                 .post(SessionValidateHandler.Path, sessValidateHandler) | ||||||
|  |                 .get(SessionTpidGetValidatedHandler.Path, SaneHandler.around(new SessionTpidGetValidatedHandler(m.getSession()))) | ||||||
|  |                 .post(SessionTpidBindHandler.Path, SaneHandler.around(new SessionTpidBindHandler(m.getSession(), m.getInvitationManager()))) | ||||||
|  |                 .post(SessionTpidUnbindHandler.Path, SaneHandler.around(new SessionTpidUnbindHandler(m.getSession()))) | ||||||
|  |  | ||||||
|  |                 // Profile endpoints | ||||||
|  |                 .get(ProfileHandler.Path, SaneHandler.around(new ProfileHandler(m.getProfile()))) | ||||||
|  |                 .get(InternalProfileHandler.Path, SaneHandler.around(new InternalProfileHandler(m.getProfile()))) | ||||||
|  |  | ||||||
|  |                 // Application Service endpoints | ||||||
|  |                 .get("/_matrix/app/v1/users/**", asNotFoundHandler) | ||||||
|  |                 .get("/users/**", asNotFoundHandler) // Legacy endpoint | ||||||
|  |                 .get("/_matrix/app/v1/rooms/**", asNotFoundHandler) | ||||||
|  |                 .get("/rooms/**", asNotFoundHandler) // Legacy endpoint | ||||||
|  |                 .put(AsTransactionHandler.Path, asTxnHandler) | ||||||
|  |                 .put("/transactions/{" + AsTransactionHandler.ID + "}", asTxnHandler) // Legacy endpoint | ||||||
|  |  | ||||||
|  |         ).build(); | ||||||
|  |  | ||||||
|  |         httpSrv.start(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void stop() { | ||||||
|  |         httpSrv.stop(); | ||||||
|  |         m.stop(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
							
								
								
									
										174
									
								
								src/main/java/io/kamax/mxisd/Mxisd.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										174
									
								
								src/main/java/io/kamax/mxisd/Mxisd.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,174 @@ | |||||||
|  | /* | ||||||
|  |  * mxisd - Matrix Identity Server Daemon | ||||||
|  |  * 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.mxisd; | ||||||
|  |  | ||||||
|  | import io.kamax.matrix.crypto.KeyManager; | ||||||
|  | import io.kamax.matrix.crypto.SignatureManager; | ||||||
|  | import io.kamax.mxisd.as.AppSvcManager; | ||||||
|  | import io.kamax.mxisd.auth.AuthManager; | ||||||
|  | import io.kamax.mxisd.auth.AuthProviders; | ||||||
|  | import io.kamax.mxisd.backend.IdentityStoreSupplier; | ||||||
|  | import io.kamax.mxisd.backend.sql.synapse.Synapse; | ||||||
|  | import io.kamax.mxisd.config.MxisdConfig; | ||||||
|  | import io.kamax.mxisd.crypto.CryptoFactory; | ||||||
|  | import io.kamax.mxisd.directory.DirectoryManager; | ||||||
|  | import io.kamax.mxisd.directory.DirectoryProviders; | ||||||
|  | import io.kamax.mxisd.dns.ClientDnsOverwrite; | ||||||
|  | import io.kamax.mxisd.dns.FederationDnsOverwrite; | ||||||
|  | import io.kamax.mxisd.invitation.InvitationManager; | ||||||
|  | import io.kamax.mxisd.lookup.ThreePidProviders; | ||||||
|  | import io.kamax.mxisd.lookup.fetcher.IRemoteIdentityServerFetcher; | ||||||
|  | import io.kamax.mxisd.lookup.provider.BridgeFetcher; | ||||||
|  | import io.kamax.mxisd.lookup.provider.RemoteIdentityServerFetcher; | ||||||
|  | import io.kamax.mxisd.lookup.strategy.LookupStrategy; | ||||||
|  | import io.kamax.mxisd.lookup.strategy.RecursivePriorityLookupStrategy; | ||||||
|  | import io.kamax.mxisd.matrix.IdentityServerUtils; | ||||||
|  | import io.kamax.mxisd.notification.NotificationHandlerSupplier; | ||||||
|  | import io.kamax.mxisd.notification.NotificationHandlers; | ||||||
|  | import io.kamax.mxisd.notification.NotificationManager; | ||||||
|  | import io.kamax.mxisd.profile.ProfileManager; | ||||||
|  | import io.kamax.mxisd.profile.ProfileProviders; | ||||||
|  | import io.kamax.mxisd.session.SessionManager; | ||||||
|  | import io.kamax.mxisd.storage.IStorage; | ||||||
|  | import io.kamax.mxisd.storage.ormlite.OrmLiteSqlStorage; | ||||||
|  | import org.apache.http.impl.client.CloseableHttpClient; | ||||||
|  | import org.apache.http.impl.client.HttpClients; | ||||||
|  |  | ||||||
|  | import java.util.ServiceLoader; | ||||||
|  |  | ||||||
|  | public class Mxisd { | ||||||
|  |  | ||||||
|  |     private MxisdConfig cfg; | ||||||
|  |  | ||||||
|  |     private CloseableHttpClient httpClient; | ||||||
|  |     private IRemoteIdentityServerFetcher srvFetcher; | ||||||
|  |  | ||||||
|  |     private IStorage store; | ||||||
|  |  | ||||||
|  |     private KeyManager keyMgr; | ||||||
|  |     private SignatureManager signMgr; | ||||||
|  |  | ||||||
|  |     // Features | ||||||
|  |     private AuthManager authMgr; | ||||||
|  |     private DirectoryManager dirMgr; | ||||||
|  |     private LookupStrategy idStrategy; | ||||||
|  |     private InvitationManager invMgr; | ||||||
|  |     private ProfileManager pMgr; | ||||||
|  |     private AppSvcManager asHander; | ||||||
|  |     private SessionManager sessMgr; | ||||||
|  |     private NotificationManager notifMgr; | ||||||
|  |  | ||||||
|  |     public Mxisd(MxisdConfig cfg) { | ||||||
|  |         this.cfg = cfg.build(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private void build() { | ||||||
|  |         httpClient = HttpClients.custom() | ||||||
|  |                 .setUserAgent("mxisd") | ||||||
|  |                 .setMaxConnPerRoute(Integer.MAX_VALUE) | ||||||
|  |                 .setMaxConnTotal(Integer.MAX_VALUE) | ||||||
|  |                 .build(); | ||||||
|  |  | ||||||
|  |         IdentityServerUtils.setHttpClient(httpClient); | ||||||
|  |         srvFetcher = new RemoteIdentityServerFetcher(httpClient); | ||||||
|  |  | ||||||
|  |         store = new OrmLiteSqlStorage(cfg); | ||||||
|  |         keyMgr = CryptoFactory.getKeyManager(cfg.getKey()); | ||||||
|  |         signMgr = CryptoFactory.getSignatureManager(keyMgr, cfg.getServer()); | ||||||
|  |         ClientDnsOverwrite clientDns = new ClientDnsOverwrite(cfg.getDns().getOverwrite()); | ||||||
|  |         FederationDnsOverwrite fedDns = new FederationDnsOverwrite(cfg.getDns().getOverwrite()); | ||||||
|  |         Synapse synapse = new Synapse(cfg.getSynapseSql()); | ||||||
|  |         BridgeFetcher bridgeFetcher = new BridgeFetcher(cfg.getLookup().getRecursive().getBridge(), srvFetcher); | ||||||
|  |  | ||||||
|  |         ServiceLoader.load(IdentityStoreSupplier.class).iterator().forEachRemaining(p -> p.accept(this)); | ||||||
|  |         ServiceLoader.load(NotificationHandlerSupplier.class).iterator().forEachRemaining(p -> p.accept(this)); | ||||||
|  |  | ||||||
|  |         idStrategy = new RecursivePriorityLookupStrategy(cfg.getLookup(), ThreePidProviders.get(), bridgeFetcher); | ||||||
|  |         pMgr = new ProfileManager(ProfileProviders.get(), clientDns, httpClient); | ||||||
|  |         notifMgr = new NotificationManager(cfg.getNotification(), NotificationHandlers.get()); | ||||||
|  |         sessMgr = new SessionManager(cfg.getSession(), cfg.getMatrix(), store, notifMgr, idStrategy, httpClient); | ||||||
|  |         invMgr = new InvitationManager(cfg.getInvite(), store, idStrategy, signMgr, fedDns, notifMgr); | ||||||
|  |         authMgr = new AuthManager(cfg, AuthProviders.get(), idStrategy, invMgr, clientDns, httpClient); | ||||||
|  |         dirMgr = new DirectoryManager(cfg.getDirectory(), clientDns, httpClient, DirectoryProviders.get()); | ||||||
|  |         asHander = new AppSvcManager(cfg, store, pMgr, notifMgr, synapse); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public MxisdConfig getConfig() { | ||||||
|  |         return cfg; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public CloseableHttpClient getHttpClient() { | ||||||
|  |         return httpClient; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public IRemoteIdentityServerFetcher getServerFetcher() { | ||||||
|  |         return srvFetcher; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public KeyManager getKeyManager() { | ||||||
|  |         return keyMgr; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public InvitationManager getInvitationManager() { | ||||||
|  |         return invMgr; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public LookupStrategy getIdentity() { | ||||||
|  |         return idStrategy; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public AuthManager getAuth() { | ||||||
|  |         return authMgr; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public SessionManager getSession() { | ||||||
|  |         return sessMgr; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public DirectoryManager getDirectory() { | ||||||
|  |         return dirMgr; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public ProfileManager getProfile() { | ||||||
|  |         return pMgr; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public SignatureManager getSign() { | ||||||
|  |         return signMgr; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public AppSvcManager getAs() { | ||||||
|  |         return asHander; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public NotificationManager getNotif() { | ||||||
|  |         return notifMgr; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void start() { | ||||||
|  |         build(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void stop() { | ||||||
|  |         // no-op | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
							
								
								
									
										73
									
								
								src/main/java/io/kamax/mxisd/MxisdStandaloneExec.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								src/main/java/io/kamax/mxisd/MxisdStandaloneExec.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,73 @@ | |||||||
|  | /* | ||||||
|  |  * mxisd - Matrix Identity Server Daemon | ||||||
|  |  * 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.mxisd; | ||||||
|  |  | ||||||
|  | import io.kamax.mxisd.config.MxisdConfig; | ||||||
|  | import io.kamax.mxisd.config.YamlConfigLoader; | ||||||
|  | import org.apache.commons.lang3.StringUtils; | ||||||
|  | import org.slf4j.Logger; | ||||||
|  | import org.slf4j.LoggerFactory; | ||||||
|  |  | ||||||
|  | import java.io.IOException; | ||||||
|  | import java.util.Arrays; | ||||||
|  | import java.util.Iterator; | ||||||
|  | import java.util.Objects; | ||||||
|  |  | ||||||
|  | public class MxisdStandaloneExec { | ||||||
|  |  | ||||||
|  |     private static final Logger log = LoggerFactory.getLogger(""); | ||||||
|  |  | ||||||
|  |     public static void main(String[] args) throws IOException { | ||||||
|  |         log.info("------------- mxisd starting -------------"); | ||||||
|  |         MxisdConfig cfg = null; | ||||||
|  |  | ||||||
|  |         Iterator<String> argsIt = Arrays.asList(args).iterator(); | ||||||
|  |         while (argsIt.hasNext()) { | ||||||
|  |             String arg = argsIt.next(); | ||||||
|  |             if (StringUtils.equals("-c", arg)) { | ||||||
|  |                 String cfgFile = argsIt.next(); | ||||||
|  |                 cfg = YamlConfigLoader.loadFromFile(cfgFile); | ||||||
|  |             } else { | ||||||
|  |                 log.info("Invalid argument: {}", arg); | ||||||
|  |                 System.exit(1); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (Objects.isNull(cfg)) { | ||||||
|  |             cfg = YamlConfigLoader.tryLoadFromFile("mxisd.yaml").orElseGet(MxisdConfig::new); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         try { | ||||||
|  |             HttpMxisd mxisd = new HttpMxisd(cfg); | ||||||
|  |             Runtime.getRuntime().addShutdownHook(new Thread(() -> { | ||||||
|  |                 mxisd.stop(); | ||||||
|  |                 log.info("------------- mxisd stopped -------------"); | ||||||
|  |             })); | ||||||
|  |             mxisd.start(); | ||||||
|  |  | ||||||
|  |             log.info("------------- mxisd started -------------"); | ||||||
|  |         } catch (Throwable t) { | ||||||
|  |             t.printStackTrace(); | ||||||
|  |             System.exit(1); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -29,38 +29,119 @@ import io.kamax.matrix.event.EventKey; | |||||||
| import io.kamax.matrix.json.GsonUtil; | import io.kamax.matrix.json.GsonUtil; | ||||||
| import io.kamax.mxisd.backend.sql.synapse.Synapse; | import io.kamax.mxisd.backend.sql.synapse.Synapse; | ||||||
| import io.kamax.mxisd.config.MatrixConfig; | import io.kamax.mxisd.config.MatrixConfig; | ||||||
|  | import io.kamax.mxisd.config.MxisdConfig; | ||||||
|  | import io.kamax.mxisd.exception.HttpMatrixException; | ||||||
|  | import io.kamax.mxisd.exception.NotAllowedException; | ||||||
| import io.kamax.mxisd.notification.NotificationManager; | import io.kamax.mxisd.notification.NotificationManager; | ||||||
| import io.kamax.mxisd.profile.ProfileManager; | import io.kamax.mxisd.profile.ProfileManager; | ||||||
|  | import io.kamax.mxisd.storage.IStorage; | ||||||
|  | import io.kamax.mxisd.storage.ormlite.dao.ASTransactionDao; | ||||||
|  | import io.kamax.mxisd.util.GsonParser; | ||||||
| import org.apache.commons.lang.StringUtils; | import org.apache.commons.lang.StringUtils; | ||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
| import org.springframework.beans.factory.annotation.Autowired; |  | ||||||
| import org.springframework.stereotype.Component; |  | ||||||
| 
 | 
 | ||||||
| import java.util.HashMap; | import java.io.InputStream; | ||||||
| import java.util.List; | import java.time.Instant; | ||||||
| import java.util.Map; | import java.util.*; | ||||||
|  | import java.util.concurrent.CompletableFuture; | ||||||
|  | import java.util.concurrent.ConcurrentHashMap; | ||||||
| import java.util.stream.Collectors; | import java.util.stream.Collectors; | ||||||
| 
 | 
 | ||||||
| @Component | public class AppSvcManager { | ||||||
| public class AppServiceHandler { |  | ||||||
| 
 | 
 | ||||||
|     private final Logger log = LoggerFactory.getLogger(AppServiceHandler.class); |     private transient final Logger log = LoggerFactory.getLogger(AppSvcManager.class); | ||||||
|  | 
 | ||||||
|  |     private final GsonParser parser; | ||||||
| 
 | 
 | ||||||
|     private MatrixConfig cfg; |     private MatrixConfig cfg; | ||||||
|  |     private IStorage store; | ||||||
|     private ProfileManager profiler; |     private ProfileManager profiler; | ||||||
|     private NotificationManager notif; |     private NotificationManager notif; | ||||||
|     private Synapse synapse; |     private Synapse synapse; | ||||||
| 
 | 
 | ||||||
|     @Autowired |     private Map<String, CompletableFuture<String>> transactionsInProgress; | ||||||
|     public AppServiceHandler(MatrixConfig cfg, ProfileManager profiler, NotificationManager notif, Synapse synapse) { | 
 | ||||||
|         this.cfg = cfg; |     public AppSvcManager(MxisdConfig cfg, IStorage store, ProfileManager profiler, NotificationManager notif, Synapse synapse) { | ||||||
|  |         this.cfg = cfg.getMatrix(); | ||||||
|  |         this.store = store; | ||||||
|         this.profiler = profiler; |         this.profiler = profiler; | ||||||
|         this.notif = notif; |         this.notif = notif; | ||||||
|         this.synapse = synapse; |         this.synapse = synapse; | ||||||
|  | 
 | ||||||
|  |         parser = new GsonParser(); | ||||||
|  |         transactionsInProgress = new ConcurrentHashMap<>(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public AppSvcManager withToken(String token) { | ||||||
|  |         if (StringUtils.isBlank(token)) { | ||||||
|  |             throw new HttpMatrixException(401, "M_UNAUTHORIZED", "No HS token"); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (!StringUtils.equals(cfg.getListener().getToken().getHs(), token)) { | ||||||
|  |             throw new NotAllowedException("Invalid HS token"); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return this; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public CompletableFuture<String> processTransaction(String txnId, InputStream is) { | ||||||
|  |         if (StringUtils.isEmpty(txnId)) { | ||||||
|  |             throw new IllegalArgumentException("Transaction ID cannot be empty"); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         synchronized (this) { | ||||||
|  |             Optional<ASTransactionDao> dao = store.getTransactionResult(cfg.getListener().getLocalpart(), txnId); | ||||||
|  |             if (dao.isPresent()) { | ||||||
|  |                 log.info("AS Transaction {} already processed - returning computed result", txnId); | ||||||
|  |                 return CompletableFuture.completedFuture(dao.get().getResult()); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             CompletableFuture<String> f = transactionsInProgress.get(txnId); | ||||||
|  |             if (Objects.nonNull(f)) { | ||||||
|  |                 log.info("Returning future for transaction {}", txnId); | ||||||
|  |                 return f; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             transactionsInProgress.put(txnId, new CompletableFuture<>()); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         CompletableFuture<String> future = transactionsInProgress.get(txnId); | ||||||
|  | 
 | ||||||
|  |         Instant start = Instant.now(); | ||||||
|  |         log.info("Processing AS Transaction {}: start", txnId); | ||||||
|  |         try { | ||||||
|  |             List<JsonObject> events = GsonUtil.asList(GsonUtil.getArray(parser.parse(is), "events"), JsonObject.class); | ||||||
|  |             is.close(); | ||||||
|  |             log.debug("{} event(s) parsed", events.size()); | ||||||
|  | 
 | ||||||
|  |             processTransaction(events); | ||||||
|  | 
 | ||||||
|  |             Instant end = Instant.now(); | ||||||
|  |             String result = "{}"; | ||||||
|  | 
 | ||||||
|  |             try { | ||||||
|  |                 log.info("Saving transaction details to store"); | ||||||
|  |                 store.insertTransactionResult(cfg.getListener().getLocalpart(), txnId, end, result); | ||||||
|  |             } finally { | ||||||
|  |                 log.debug("Removing CompletedFuture from transaction map"); | ||||||
|  |                 transactionsInProgress.remove(txnId); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             log.info("Processed AS transaction {} in {} ms", txnId, (Instant.now().toEpochMilli() - start.toEpochMilli())); | ||||||
|  |             future.complete(result); | ||||||
|  |         } catch (Exception e) { | ||||||
|  |             log.error("Unable to properly process transaction {}", txnId, e); | ||||||
|  |             future.completeExceptionally(e); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         log.info("Processing AS Transaction {}: end", txnId); | ||||||
|  |         return future; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public void processTransaction(List<JsonObject> eventsJson) { |     public void processTransaction(List<JsonObject> eventsJson) { | ||||||
|  |         log.info("Processing transaction events: start"); | ||||||
|  | 
 | ||||||
|         eventsJson.forEach(ev -> { |         eventsJson.forEach(ev -> { | ||||||
|             String evId = EventKey.Id.getStringOrNull(ev); |             String evId = EventKey.Id.getStringOrNull(ev); | ||||||
|             if (StringUtils.isBlank(evId)) { |             if (StringUtils.isBlank(evId)) { | ||||||
| @@ -78,10 +159,11 @@ public class AppServiceHandler { | |||||||
| 
 | 
 | ||||||
|             String senderId = EventKey.Sender.getStringOrNull(ev); |             String senderId = EventKey.Sender.getStringOrNull(ev); | ||||||
|             if (StringUtils.isBlank(senderId)) { |             if (StringUtils.isBlank(senderId)) { | ||||||
|                 log.debug("Event has no room ID, skipping"); |                 log.debug("Event has no sender ID, skipping"); | ||||||
|                 return; |                 return; | ||||||
|             } |             } | ||||||
|             _MatrixID sender = MatrixID.asAcceptable(senderId); |             _MatrixID sender = MatrixID.asAcceptable(senderId); | ||||||
|  |             log.debug("Sender: {}", senderId); | ||||||
| 
 | 
 | ||||||
|             if (!StringUtils.equals("m.room.member", GsonUtil.getStringOrNull(ev, "type"))) { |             if (!StringUtils.equals("m.room.member", GsonUtil.getStringOrNull(ev, "type"))) { | ||||||
|                 log.debug("This is not a room membership event, skipping"); |                 log.debug("This is not a room membership event, skipping"); | ||||||
| @@ -105,7 +187,7 @@ public class AppServiceHandler { | |||||||
|                 return; |                 return; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             log.info("Got invite for {}", inviteeId); |             log.info("Got invite from {} to {}", senderId, inviteeId); | ||||||
| 
 | 
 | ||||||
|             boolean wasSent = false; |             boolean wasSent = false; | ||||||
|             List<_ThreePid> tpids = profiler.getThreepids(invitee).stream() |             List<_ThreePid> tpids = profiler.getThreepids(invitee).stream() | ||||||
| @@ -121,7 +203,7 @@ public class AppServiceHandler { | |||||||
|                     synapse.getRoomName(roomId).ifPresent(name -> properties.put("room_name", name)); |                     synapse.getRoomName(roomId).ifPresent(name -> properties.put("room_name", name)); | ||||||
|                 } catch (RuntimeException e) { |                 } catch (RuntimeException e) { | ||||||
|                     log.warn("Could not fetch room name", e); |                     log.warn("Could not fetch room name", e); | ||||||
|                     log.warn("Unable to fetch room name: Did you integrate your Homeserver as documented?"); |                     log.info("Unable to fetch room name: Did you integrate your Homeserver as documented?"); | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 IMatrixIdInvite inv = new MatrixIdInvite(roomId, sender, invitee, tpid.getMedium(), tpid.getAddress(), properties); |                 IMatrixIdInvite inv = new MatrixIdInvite(roomId, sender, invitee, tpid.getMedium(), tpid.getAddress(), properties); | ||||||
| @@ -134,6 +216,8 @@ public class AppServiceHandler { | |||||||
| 
 | 
 | ||||||
|             log.debug("Event {}: processing end", evId); |             log.debug("Event {}: processing end", evId); | ||||||
|         }); |         }); | ||||||
|  | 
 | ||||||
|  |         log.info("Processing transaction events: end"); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
| @@ -20,37 +20,92 @@ | |||||||
|  |  | ||||||
| package io.kamax.mxisd.auth; | package io.kamax.mxisd.auth; | ||||||
|  |  | ||||||
|  | import com.google.gson.Gson; | ||||||
|  | import com.google.gson.JsonObject; | ||||||
|  | import com.google.gson.JsonParser; | ||||||
|  | import com.google.gson.JsonSyntaxException; | ||||||
|  | import com.google.i18n.phonenumbers.NumberParseException; | ||||||
|  | import com.google.i18n.phonenumbers.PhoneNumberUtil; | ||||||
|  | import com.google.i18n.phonenumbers.Phonenumber; | ||||||
| import io.kamax.matrix.MatrixID; | import io.kamax.matrix.MatrixID; | ||||||
| import io.kamax.matrix.ThreePid; | import io.kamax.matrix.ThreePid; | ||||||
| import io.kamax.matrix._MatrixID; | import io.kamax.matrix._MatrixID; | ||||||
| import io.kamax.matrix._ThreePid; | import io.kamax.matrix._ThreePid; | ||||||
|  | import io.kamax.matrix.json.GsonUtil; | ||||||
| import io.kamax.mxisd.UserIdType; | import io.kamax.mxisd.UserIdType; | ||||||
| import io.kamax.mxisd.auth.provider.AuthenticatorProvider; | import io.kamax.mxisd.auth.provider.AuthenticatorProvider; | ||||||
| import io.kamax.mxisd.auth.provider.BackendAuthResult; | import io.kamax.mxisd.auth.provider.BackendAuthResult; | ||||||
|  | import io.kamax.mxisd.config.AuthenticationConfig; | ||||||
| import io.kamax.mxisd.config.MatrixConfig; | import io.kamax.mxisd.config.MatrixConfig; | ||||||
|  | import io.kamax.mxisd.config.MxisdConfig; | ||||||
|  | import io.kamax.mxisd.dns.ClientDnsOverwrite; | ||||||
|  | import io.kamax.mxisd.exception.RemoteLoginException; | ||||||
| import io.kamax.mxisd.invitation.InvitationManager; | import io.kamax.mxisd.invitation.InvitationManager; | ||||||
| import io.kamax.mxisd.lookup.ThreePidMapping; | import io.kamax.mxisd.lookup.ThreePidMapping; | ||||||
|  | import io.kamax.mxisd.lookup.strategy.LookupStrategy; | ||||||
|  | import io.kamax.mxisd.util.RestClientUtils; | ||||||
|  | import org.apache.commons.io.IOUtils; | ||||||
|  | import org.apache.commons.lang.StringUtils; | ||||||
|  | import org.apache.http.HttpEntity; | ||||||
|  | import org.apache.http.client.methods.CloseableHttpResponse; | ||||||
|  | import org.apache.http.client.methods.HttpPost; | ||||||
|  | import org.apache.http.client.utils.URIBuilder; | ||||||
|  | import org.apache.http.impl.client.CloseableHttpClient; | ||||||
|  | import org.apache.http.util.EntityUtils; | ||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
| import org.springframework.beans.factory.annotation.Autowired; |  | ||||||
| import org.springframework.stereotype.Service; |  | ||||||
|  |  | ||||||
|  | import java.io.IOException; | ||||||
|  | import java.net.URI; | ||||||
|  | import java.nio.charset.StandardCharsets; | ||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
| import java.util.List; | import java.util.List; | ||||||
|  | import java.util.Objects; | ||||||
|  |  | ||||||
| @Service |  | ||||||
| public class AuthManager { | public class AuthManager { | ||||||
|  |  | ||||||
|     private Logger log = LoggerFactory.getLogger(AuthManager.class); |     private static final String TypeKey = "type"; | ||||||
|  |     private static final String UserKey = "user"; | ||||||
|  |     private static final String IdentifierKey = "identifier"; | ||||||
|  |     private static final String ThreepidMediumKey = "medium"; | ||||||
|  |     private static final String ThreepidAddressKey = "address"; | ||||||
|  |     private static final String UserIdTypeValue = "m.id.user"; | ||||||
|  |     private static final String ThreepidTypeValue = "m.id.thirdparty"; | ||||||
|  |  | ||||||
|     @Autowired |     private transient final Logger log = LoggerFactory.getLogger(AuthManager.class); | ||||||
|     private List<AuthenticatorProvider> providers = new ArrayList<>(); |     private final Gson gson = GsonUtil.get(); // FIXME replace | ||||||
|  |  | ||||||
|     @Autowired |     private List<AuthenticatorProvider> providers; | ||||||
|     private MatrixConfig mxCfg; |     private MatrixConfig mxCfg; | ||||||
|  |     private AuthenticationConfig cfg; | ||||||
|     @Autowired |  | ||||||
|     private InvitationManager invMgr; |     private InvitationManager invMgr; | ||||||
|  |     private ClientDnsOverwrite dns; | ||||||
|  |     private LookupStrategy strategy; | ||||||
|  |     private CloseableHttpClient client; | ||||||
|  |  | ||||||
|  |     public AuthManager( | ||||||
|  |             MxisdConfig cfg, | ||||||
|  |             List<? extends AuthenticatorProvider> providers, | ||||||
|  |             LookupStrategy strategy, | ||||||
|  |             InvitationManager invMgr, | ||||||
|  |             ClientDnsOverwrite dns, | ||||||
|  |             CloseableHttpClient client | ||||||
|  |     ) { | ||||||
|  |         this.cfg = cfg.getAuth(); | ||||||
|  |         this.mxCfg = cfg.getMatrix(); | ||||||
|  |         this.providers = new ArrayList<>(providers); | ||||||
|  |         this.strategy = strategy; | ||||||
|  |         this.invMgr = invMgr; | ||||||
|  |         this.dns = dns; | ||||||
|  |         this.client = client; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public String resolveProxyUrl(URI target) { | ||||||
|  |         URIBuilder builder = dns.transform(target); | ||||||
|  |         String urlToLogin = builder.toString(); | ||||||
|  |         log.info("Proxy resolution: {} to {}", target.toString(), urlToLogin); | ||||||
|  |         return urlToLogin; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     public UserAuthResult authenticate(String id, String password) { |     public UserAuthResult authenticate(String id, String password) { | ||||||
|         _MatrixID mxid = MatrixID.asAcceptable(id); |         _MatrixID mxid = MatrixID.asAcceptable(id); | ||||||
| @@ -92,4 +147,128 @@ public class AuthManager { | |||||||
|         return new UserAuthResult().failure(); |         return new UserAuthResult().failure(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     public String proxyLogin(URI target, String body) { | ||||||
|  |         JsonObject reqJsonObject = io.kamax.matrix.json.GsonUtil.parseObj(body); | ||||||
|  |  | ||||||
|  |         GsonUtil.findObj(reqJsonObject, IdentifierKey).ifPresent(obj -> { | ||||||
|  |             GsonUtil.findString(obj, TypeKey).ifPresent(type -> { | ||||||
|  |                 if (StringUtils.equals(type, UserIdTypeValue)) { | ||||||
|  |                     log.info("Login request is User ID type"); | ||||||
|  |  | ||||||
|  |                     if (cfg.getRewrite().getUser().getRules().isEmpty()) { | ||||||
|  |                         log.info("No User ID rewrite rules to apply"); | ||||||
|  |                     } else { | ||||||
|  |                         log.info("User ID rewrite rules: checking for a match"); | ||||||
|  |  | ||||||
|  |                         String userId = GsonUtil.getStringOrThrow(obj, UserKey); | ||||||
|  |                         for (AuthenticationConfig.Rule m : cfg.getRewrite().getUser().getRules()) { | ||||||
|  |                             if (m.getPattern().matcher(userId).matches()) { | ||||||
|  |                                 log.info("Found matching pattern, resolving to 3PID with medium {}", m.getMedium()); | ||||||
|  |  | ||||||
|  |                                 // Remove deprecated login info on the top object if exists to avoid duplication | ||||||
|  |                                 reqJsonObject.remove(UserKey); | ||||||
|  |                                 obj.addProperty(TypeKey, ThreepidTypeValue); | ||||||
|  |                                 obj.addProperty(ThreepidMediumKey, m.getMedium()); | ||||||
|  |                                 obj.addProperty(ThreepidAddressKey, userId); | ||||||
|  |  | ||||||
|  |                                 log.info("Rewrite to 3PID done"); | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |  | ||||||
|  |                         log.info("User ID rewrite rules: done checking rules"); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         GsonUtil.findObj(reqJsonObject, IdentifierKey).ifPresent(obj -> { | ||||||
|  |             GsonUtil.findString(obj, TypeKey).ifPresent(type -> { | ||||||
|  |                 if (StringUtils.equals(type, ThreepidTypeValue)) { | ||||||
|  |                     // Remove deprecated login info if exists to avoid duplication | ||||||
|  |                     reqJsonObject.remove(ThreepidMediumKey); | ||||||
|  |                     reqJsonObject.remove(ThreepidAddressKey); | ||||||
|  |  | ||||||
|  |                     GsonUtil.findPrimitive(obj, ThreepidMediumKey).ifPresent(medium -> { | ||||||
|  |                         GsonUtil.findPrimitive(obj, ThreepidAddressKey).ifPresent(address -> { | ||||||
|  |                             log.info("Login request with medium '{}' and address '{}'", medium.getAsString(), address.getAsString()); | ||||||
|  |                             strategy.findLocal(medium.getAsString(), address.getAsString()).ifPresent(lookupDataOpt -> { | ||||||
|  |                                 obj.remove(ThreepidMediumKey); | ||||||
|  |                                 obj.remove(ThreepidAddressKey); | ||||||
|  |                                 obj.addProperty(TypeKey, UserIdTypeValue); | ||||||
|  |                                 obj.addProperty(UserKey, lookupDataOpt.getMxid().getLocalPart()); | ||||||
|  |                             }); | ||||||
|  |                         }); | ||||||
|  |                     }); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 if (StringUtils.equals(type, "m.id.phone")) { | ||||||
|  |                     // Remove deprecated login info if exists to avoid duplication | ||||||
|  |                     reqJsonObject.remove(ThreepidMediumKey); | ||||||
|  |                     reqJsonObject.remove(ThreepidAddressKey); | ||||||
|  |  | ||||||
|  |                     GsonUtil.findPrimitive(obj, "number").ifPresent(number -> { | ||||||
|  |                         GsonUtil.findPrimitive(obj, "country").ifPresent(country -> { | ||||||
|  |                             log.info("Login request with phone '{}'-'{}'", country.getAsString(), number.getAsString()); | ||||||
|  |                             try { | ||||||
|  |                                 PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance(); | ||||||
|  |                                 Phonenumber.PhoneNumber phoneNumber = phoneUtil.parse(number.getAsString(), country.getAsString()); | ||||||
|  |                                 String msisdn = phoneUtil.format(phoneNumber, PhoneNumberUtil.PhoneNumberFormat.E164).replace("+", ""); | ||||||
|  |                                 String medium = "msisdn"; | ||||||
|  |                                 strategy.findLocal(medium, msisdn).ifPresent(lookupDataOpt -> { | ||||||
|  |                                     obj.remove("country"); | ||||||
|  |                                     obj.remove("number"); | ||||||
|  |                                     obj.addProperty(TypeKey, UserIdTypeValue); | ||||||
|  |                                     obj.addProperty(UserKey, lookupDataOpt.getMxid().getLocalPart()); | ||||||
|  |                                 }); | ||||||
|  |                             } catch (NumberParseException e) { | ||||||
|  |                                 log.error("Not a valid phone number"); | ||||||
|  |                                 throw new RuntimeException(e); | ||||||
|  |                             } | ||||||
|  |                         }); | ||||||
|  |                     }); | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         // invoke 'login' on homeserver | ||||||
|  |         HttpPost httpPost = RestClientUtils.post(resolveProxyUrl(target), gson, reqJsonObject); | ||||||
|  |         try (CloseableHttpResponse httpResponse = client.execute(httpPost)) { | ||||||
|  |             // check http status | ||||||
|  |             int status = httpResponse.getStatusLine().getStatusCode(); | ||||||
|  |             log.info("http status = {}", status); | ||||||
|  |             if (status != 200) { | ||||||
|  |                 // try to get possible json error message from response | ||||||
|  |                 // otherwise just get returned plain error message | ||||||
|  |                 String errcode = String.valueOf(httpResponse.getStatusLine().getStatusCode()); | ||||||
|  |                 String error = EntityUtils.toString(httpResponse.getEntity()); | ||||||
|  |                 if (httpResponse.getEntity() != null) { | ||||||
|  |                     try { | ||||||
|  |                         JsonObject bodyJson = new JsonParser().parse(error).getAsJsonObject(); | ||||||
|  |                         if (bodyJson.has("errcode")) { | ||||||
|  |                             errcode = bodyJson.get("errcode").getAsString(); | ||||||
|  |                         } | ||||||
|  |                         if (bodyJson.has("error")) { | ||||||
|  |                             error = bodyJson.get("error").getAsString(); | ||||||
|  |                         } | ||||||
|  |                         throw new RemoteLoginException(status, errcode, error, bodyJson); | ||||||
|  |                     } catch (JsonSyntaxException e) { | ||||||
|  |                         log.warn("Response body is not JSON"); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 throw new RemoteLoginException(status, errcode, error); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             // return response | ||||||
|  |             HttpEntity entity = httpResponse.getEntity(); | ||||||
|  |             if (Objects.isNull(entity)) { | ||||||
|  |                 log.warn("Expected HS to return data but got nothing"); | ||||||
|  |                 return ""; | ||||||
|  |             } else { | ||||||
|  |                 return IOUtils.toString(httpResponse.getEntity().getContent(), StandardCharsets.UTF_8); | ||||||
|  |             } | ||||||
|  |         } catch (IOException e) { | ||||||
|  |             throw new RuntimeException(e); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -18,20 +18,25 @@ | |||||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. |  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package io.kamax.mxisd.controller.identity.v1.remote; | package io.kamax.mxisd.auth; | ||||||
| 
 | 
 | ||||||
| public class RemoteIdentityAPIv1 { | import io.kamax.mxisd.auth.provider.AuthenticatorProvider; | ||||||
| 
 | 
 | ||||||
|     public static final String BASE = "/_matrix/identity/remote/api/v1"; | import java.util.ArrayList; | ||||||
|     public static final String SESSION_REQUEST_TOKEN = BASE + "/validate/requestToken"; | import java.util.List; | ||||||
|     public static final String SESSION_CHECK = BASE + "/validate/check"; | import java.util.function.Supplier; | ||||||
|  | import java.util.stream.Collectors; | ||||||
| 
 | 
 | ||||||
|     public static String getRequestToken(String id, String secret) { | public class AuthProviders { | ||||||
|         return SESSION_REQUEST_TOKEN + "?sid=" + id + "&client_secret=" + secret; | 
 | ||||||
|  |     private static final List<Supplier<? extends AuthenticatorProvider>> suppliers = new ArrayList<>(); | ||||||
|  | 
 | ||||||
|  |     public static void register(Supplier<? extends AuthenticatorProvider> supplier) { | ||||||
|  |         suppliers.add(supplier); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public static String getSessionCheck(String id, String secret) { |     public static List<? extends AuthenticatorProvider> get() { | ||||||
|         return SESSION_CHECK + "?sid=" + id + "&client_secret=" + secret; |         return suppliers.stream().map(Supplier::get).collect(Collectors.toList()); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
| @@ -0,0 +1,29 @@ | |||||||
|  | /* | ||||||
|  |  * mxisd - Matrix Identity Server Daemon | ||||||
|  |  * 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.mxisd.backend; | ||||||
|  |  | ||||||
|  | import io.kamax.mxisd.Mxisd; | ||||||
|  |  | ||||||
|  | import java.util.function.Consumer; | ||||||
|  |  | ||||||
|  | public interface IdentityStoreSupplier extends Consumer<Mxisd> { | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -33,20 +33,16 @@ import io.kamax.mxisd.exception.InternalServerError; | |||||||
| import org.apache.commons.lang3.StringUtils; | import org.apache.commons.lang3.StringUtils; | ||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
| import org.springframework.beans.factory.annotation.Autowired; |  | ||||||
| import org.springframework.stereotype.Component; |  | ||||||
|  |  | ||||||
| import java.util.Objects; | import java.util.Objects; | ||||||
| import java.util.Optional; | import java.util.Optional; | ||||||
|  |  | ||||||
| @Component |  | ||||||
| public class ExecAuthStore extends ExecStore implements AuthenticatorProvider { | public class ExecAuthStore extends ExecStore implements AuthenticatorProvider { | ||||||
|  |  | ||||||
|     private final Logger log = LoggerFactory.getLogger(ExecAuthStore.class); |     private transient final Logger log = LoggerFactory.getLogger(ExecAuthStore.class); | ||||||
|  |  | ||||||
|     private ExecConfig.Auth cfg; |     private ExecConfig.Auth cfg; | ||||||
|  |  | ||||||
|     @Autowired |  | ||||||
|     public ExecAuthStore(ExecConfig cfg) { |     public ExecAuthStore(ExecConfig cfg) { | ||||||
|         this.cfg = Objects.requireNonNull(cfg.getAuth()); |         this.cfg = Objects.requireNonNull(cfg.getAuth()); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -24,22 +24,19 @@ import io.kamax.matrix.MatrixID; | |||||||
| import io.kamax.matrix.json.GsonUtil; | import io.kamax.matrix.json.GsonUtil; | ||||||
| import io.kamax.mxisd.config.ExecConfig; | import io.kamax.mxisd.config.ExecConfig; | ||||||
| import io.kamax.mxisd.config.MatrixConfig; | import io.kamax.mxisd.config.MatrixConfig; | ||||||
| import io.kamax.mxisd.controller.directory.v1.io.UserDirectorySearchRequest; | import io.kamax.mxisd.config.MxisdConfig; | ||||||
| import io.kamax.mxisd.controller.directory.v1.io.UserDirectorySearchResult; | import io.kamax.mxisd.directory.DirectoryProvider; | ||||||
| import io.kamax.mxisd.directory.IDirectoryProvider; | import io.kamax.mxisd.http.io.UserDirectorySearchRequest; | ||||||
|  | import io.kamax.mxisd.http.io.UserDirectorySearchResult; | ||||||
| import org.apache.commons.lang3.StringUtils; | import org.apache.commons.lang3.StringUtils; | ||||||
| import org.springframework.beans.factory.annotation.Autowired; |  | ||||||
| import org.springframework.stereotype.Component; |  | ||||||
|  |  | ||||||
| @Component | public class ExecDirectoryStore extends ExecStore implements DirectoryProvider { | ||||||
| public class ExecDirectoryStore extends ExecStore implements IDirectoryProvider { |  | ||||||
|  |  | ||||||
|     private ExecConfig.Directory cfg; |     private ExecConfig.Directory cfg; | ||||||
|     private MatrixConfig mxCfg; |     private MatrixConfig mxCfg; | ||||||
|  |  | ||||||
|     @Autowired |     public ExecDirectoryStore(MxisdConfig cfg) { | ||||||
|     public ExecDirectoryStore(ExecConfig cfg, MatrixConfig mxCfg) { |         this(cfg.getExec().getDirectory(), cfg.getMatrix()); | ||||||
|         this(cfg.getDirectory(), mxCfg); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public ExecDirectoryStore(ExecConfig.Directory cfg, MatrixConfig mxCfg) { |     public ExecDirectoryStore(ExecConfig.Directory cfg, MatrixConfig mxCfg) { | ||||||
| @@ -47,11 +44,6 @@ public class ExecDirectoryStore extends ExecStore implements IDirectoryProvider | |||||||
|         this.mxCfg = mxCfg; |         this.mxCfg = mxCfg; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public boolean isEnabled() { |  | ||||||
|         return cfg.isEnabled(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private UserDirectorySearchResult search(ExecConfig.Process cfg, UserDirectorySearchRequest request) { |     private UserDirectorySearchResult search(ExecConfig.Process cfg, UserDirectorySearchRequest request) { | ||||||
|         if (StringUtils.isEmpty(cfg.getCommand())) { |         if (StringUtils.isEmpty(cfg.getCommand())) { | ||||||
|             return UserDirectorySearchResult.empty(); |             return UserDirectorySearchResult.empty(); | ||||||
|   | |||||||
| @@ -40,8 +40,6 @@ import io.kamax.mxisd.lookup.provider.IThreePidProvider; | |||||||
| import org.apache.commons.lang3.StringUtils; | import org.apache.commons.lang3.StringUtils; | ||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
| import org.springframework.beans.factory.annotation.Autowired; |  | ||||||
| import org.springframework.stereotype.Component; |  | ||||||
|  |  | ||||||
| import java.util.Collections; | import java.util.Collections; | ||||||
| import java.util.List; | import java.util.List; | ||||||
| @@ -49,15 +47,13 @@ import java.util.Objects; | |||||||
| import java.util.Optional; | import java.util.Optional; | ||||||
| import java.util.stream.Collectors; | import java.util.stream.Collectors; | ||||||
|  |  | ||||||
| @Component |  | ||||||
| public class ExecIdentityStore extends ExecStore implements IThreePidProvider { | public class ExecIdentityStore extends ExecStore implements IThreePidProvider { | ||||||
|  |  | ||||||
|     private final Logger log = LoggerFactory.getLogger(ExecIdentityStore.class); |     private transient final Logger log = LoggerFactory.getLogger(ExecIdentityStore.class); | ||||||
|  |  | ||||||
|     private final ExecConfig.Identity cfg; |     private final ExecConfig.Identity cfg; | ||||||
|     private final MatrixConfig mxCfg; |     private final MatrixConfig mxCfg; | ||||||
|  |  | ||||||
|     @Autowired |  | ||||||
|     public ExecIdentityStore(ExecConfig cfg, MatrixConfig mxCfg) { |     public ExecIdentityStore(ExecConfig cfg, MatrixConfig mxCfg) { | ||||||
|         this(cfg.getIdentity(), mxCfg); |         this(cfg.getIdentity(), mxCfg); | ||||||
|     } |     } | ||||||
| @@ -67,11 +63,6 @@ public class ExecIdentityStore extends ExecStore implements IThreePidProvider { | |||||||
|         this.mxCfg = mxCfg; |         this.mxCfg = mxCfg; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public boolean isEnabled() { |  | ||||||
|         return cfg.isEnabled(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public boolean isLocal() { |     public boolean isLocal() { | ||||||
|         return true; |         return true; | ||||||
|   | |||||||
| @@ -0,0 +1,56 @@ | |||||||
|  | /* | ||||||
|  |  * mxisd - Matrix Identity Server Daemon | ||||||
|  |  * 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.mxisd.backend.exec; | ||||||
|  |  | ||||||
|  | import io.kamax.mxisd.Mxisd; | ||||||
|  | import io.kamax.mxisd.auth.AuthProviders; | ||||||
|  | import io.kamax.mxisd.backend.IdentityStoreSupplier; | ||||||
|  | import io.kamax.mxisd.config.MxisdConfig; | ||||||
|  | import io.kamax.mxisd.directory.DirectoryProviders; | ||||||
|  | import io.kamax.mxisd.lookup.ThreePidProviders; | ||||||
|  | import io.kamax.mxisd.profile.ProfileProviders; | ||||||
|  |  | ||||||
|  | public class ExecIdentityStoreSupplier implements IdentityStoreSupplier { | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void accept(Mxisd mxisd) { | ||||||
|  |         accept(mxisd.getConfig()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void accept(MxisdConfig cfg) { | ||||||
|  |         if (cfg.getExec().getAuth().isEnabled()) { | ||||||
|  |             AuthProviders.register(() -> new ExecAuthStore(cfg.getExec())); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (cfg.getExec().getDirectory().isEnabled()) { | ||||||
|  |             DirectoryProviders.register(() -> new ExecDirectoryStore(cfg)); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (cfg.getExec().getIdentity().isEnabled()) { | ||||||
|  |             ThreePidProviders.register(() -> new ExecIdentityStore(cfg.getExec(), cfg.getMatrix())); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (cfg.getExec().getProfile().isEnabled()) { | ||||||
|  |             ProfileProviders.register(() -> new ExecProfileStore(cfg.getExec())); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -28,19 +28,15 @@ import io.kamax.mxisd.profile.JsonProfileRequest; | |||||||
| import io.kamax.mxisd.profile.JsonProfileResult; | import io.kamax.mxisd.profile.JsonProfileResult; | ||||||
| import io.kamax.mxisd.profile.ProfileProvider; | import io.kamax.mxisd.profile.ProfileProvider; | ||||||
| import org.apache.commons.lang.StringUtils; | import org.apache.commons.lang.StringUtils; | ||||||
| import org.springframework.beans.factory.annotation.Autowired; |  | ||||||
| import org.springframework.stereotype.Component; |  | ||||||
|  |  | ||||||
| import java.util.Collections; | import java.util.Collections; | ||||||
| import java.util.List; | import java.util.List; | ||||||
| import java.util.Optional; | import java.util.Optional; | ||||||
|  |  | ||||||
| @Component |  | ||||||
| public class ExecProfileStore extends ExecStore implements ProfileProvider { | public class ExecProfileStore extends ExecStore implements ProfileProvider { | ||||||
|  |  | ||||||
|     private ExecConfig.Profile cfg; |     private ExecConfig.Profile cfg; | ||||||
|  |  | ||||||
|     @Autowired |  | ||||||
|     public ExecProfileStore(ExecConfig cfg) { |     public ExecProfileStore(ExecConfig cfg) { | ||||||
|         this(cfg.getProfile()); |         this(cfg.getProfile()); | ||||||
|     } |     } | ||||||
| @@ -49,11 +45,6 @@ public class ExecProfileStore extends ExecStore implements ProfileProvider { | |||||||
|         this.cfg = cfg; |         this.cfg = cfg; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public boolean isEnabled() { |  | ||||||
|         return cfg.isEnabled(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private Optional<JsonProfileResult> getFull(_MatrixID userId, ExecConfig.Process cfg) { |     private Optional<JsonProfileResult> getFull(_MatrixID userId, ExecConfig.Process cfg) { | ||||||
|         Processor<Optional<JsonProfileResult>> p = new Processor<>(cfg); |         Processor<Optional<JsonProfileResult>> p = new Processor<>(cfg); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -47,7 +47,7 @@ public class ExecStore { | |||||||
|         return GsonUtil.get().toJson(o); |         return GsonUtil.get().toJson(o); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private final Logger log = LoggerFactory.getLogger(ExecStore.class); |     private transient final Logger log = LoggerFactory.getLogger(ExecStore.class); | ||||||
|  |  | ||||||
|     private Supplier<ProcessExecutor> executorSupplier = () -> new ProcessExecutor().readOutput(true); |     private Supplier<ProcessExecutor> executorSupplier = () -> new ProcessExecutor().readOutput(true); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -29,6 +29,7 @@ import io.kamax.matrix._MatrixID; | |||||||
| import io.kamax.mxisd.UserIdType; | import io.kamax.mxisd.UserIdType; | ||||||
| import io.kamax.mxisd.auth.provider.AuthenticatorProvider; | import io.kamax.mxisd.auth.provider.AuthenticatorProvider; | ||||||
| import io.kamax.mxisd.auth.provider.BackendAuthResult; | import io.kamax.mxisd.auth.provider.BackendAuthResult; | ||||||
|  | import io.kamax.mxisd.config.FirebaseConfig; | ||||||
| import org.apache.commons.lang.StringUtils; | import org.apache.commons.lang.StringUtils; | ||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
| @@ -38,10 +39,14 @@ import java.util.concurrent.TimeUnit; | |||||||
|  |  | ||||||
| public class GoogleFirebaseAuthenticator extends GoogleFirebaseBackend implements AuthenticatorProvider { | public class GoogleFirebaseAuthenticator extends GoogleFirebaseBackend implements AuthenticatorProvider { | ||||||
|  |  | ||||||
|     private Logger log = LoggerFactory.getLogger(GoogleFirebaseAuthenticator.class); |     private transient final Logger log = LoggerFactory.getLogger(GoogleFirebaseAuthenticator.class); | ||||||
|  |  | ||||||
|     private PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance(); |     private PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance(); | ||||||
|  |  | ||||||
|  |     public GoogleFirebaseAuthenticator(FirebaseConfig cfg) { | ||||||
|  |         this(cfg.isEnabled(), cfg.getCredentials(), cfg.getDatabase()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     public GoogleFirebaseAuthenticator(boolean isEnabled, String credsPath, String db) { |     public GoogleFirebaseAuthenticator(boolean isEnabled, String credsPath, String db) { | ||||||
|         super(isEnabled, "AuthenticationProvider", credsPath, db); |         super(isEnabled, "AuthenticationProvider", credsPath, db); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -35,7 +35,7 @@ import java.io.IOException; | |||||||
|  |  | ||||||
| public class GoogleFirebaseBackend { | public class GoogleFirebaseBackend { | ||||||
|  |  | ||||||
|     private Logger log = LoggerFactory.getLogger(GoogleFirebaseBackend.class); |     private transient final Logger log = LoggerFactory.getLogger(GoogleFirebaseBackend.class); | ||||||
|  |  | ||||||
|     private boolean isEnabled; |     private boolean isEnabled; | ||||||
|     private FirebaseAuth fbAuth; |     private FirebaseAuth fbAuth; | ||||||
| @@ -60,7 +60,9 @@ public class GoogleFirebaseBackend { | |||||||
|  |  | ||||||
|     private FirebaseCredential getCreds(String credsPath) throws IOException { |     private FirebaseCredential getCreds(String credsPath) throws IOException { | ||||||
|         if (StringUtils.isNotBlank(credsPath)) { |         if (StringUtils.isNotBlank(credsPath)) { | ||||||
|             return FirebaseCredentials.fromCertificate(new FileInputStream(credsPath)); |             try (FileInputStream is = new FileInputStream(credsPath)) { | ||||||
|  |                 return FirebaseCredentials.fromCertificate(is); | ||||||
|  |             } | ||||||
|         } else { |         } else { | ||||||
|             return FirebaseCredentials.applicationDefault(); |             return FirebaseCredentials.applicationDefault(); | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -25,6 +25,7 @@ import com.google.firebase.tasks.OnFailureListener; | |||||||
| import com.google.firebase.tasks.OnSuccessListener; | import com.google.firebase.tasks.OnSuccessListener; | ||||||
| import io.kamax.matrix.MatrixID; | import io.kamax.matrix.MatrixID; | ||||||
| import io.kamax.matrix.ThreePidMedium; | import io.kamax.matrix.ThreePidMedium; | ||||||
|  | import io.kamax.mxisd.config.MxisdConfig; | ||||||
| import io.kamax.mxisd.lookup.SingleLookupReply; | import io.kamax.mxisd.lookup.SingleLookupReply; | ||||||
| import io.kamax.mxisd.lookup.SingleLookupRequest; | import io.kamax.mxisd.lookup.SingleLookupRequest; | ||||||
| import io.kamax.mxisd.lookup.ThreePidMapping; | import io.kamax.mxisd.lookup.ThreePidMapping; | ||||||
| @@ -40,9 +41,13 @@ import java.util.concurrent.TimeUnit; | |||||||
|  |  | ||||||
| public class GoogleFirebaseProvider extends GoogleFirebaseBackend implements IThreePidProvider { | public class GoogleFirebaseProvider extends GoogleFirebaseBackend implements IThreePidProvider { | ||||||
|  |  | ||||||
|     private Logger log = LoggerFactory.getLogger(GoogleFirebaseProvider.class); |     private transient final Logger log = LoggerFactory.getLogger(GoogleFirebaseProvider.class); | ||||||
|     private String domain; |     private String domain; | ||||||
|  |  | ||||||
|  |     public GoogleFirebaseProvider(MxisdConfig cfg) { | ||||||
|  |         this(cfg.getFirebase().isEnabled(), cfg.getFirebase().getCredentials(), cfg.getFirebase().getDatabase(), cfg.getMatrix().getDomain()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     public GoogleFirebaseProvider(boolean isEnabled, String credsPath, String db, String domain) { |     public GoogleFirebaseProvider(boolean isEnabled, String credsPath, String db, String domain) { | ||||||
|         super(isEnabled, "ThreePidProvider", credsPath, db); |         super(isEnabled, "ThreePidProvider", credsPath, db); | ||||||
|         this.domain = domain; |         this.domain = domain; | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| /* | /* | ||||||
|  * mxisd - Matrix Identity Server Daemon |  * mxisd - Matrix Identity Server Daemon | ||||||
|  * Copyright (C) 2017 Kamax Sarl |  * Copyright (C) 2018 Kamax Sarl | ||||||
|  * |  * | ||||||
|  * https://www.kamax.io/ |  * https://www.kamax.io/ | ||||||
|  * |  * | ||||||
| @@ -18,21 +18,26 @@ | |||||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. |  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package io.kamax.mxisd.spring; | package io.kamax.mxisd.backend.firebase; | ||||||
| 
 | 
 | ||||||
| import io.kamax.mxisd.exception.ConfigurationException; | import io.kamax.mxisd.Mxisd; | ||||||
| import org.springframework.boot.diagnostics.AbstractFailureAnalyzer; | import io.kamax.mxisd.auth.AuthProviders; | ||||||
| import org.springframework.boot.diagnostics.FailureAnalysis; | import io.kamax.mxisd.backend.IdentityStoreSupplier; | ||||||
|  | import io.kamax.mxisd.config.MxisdConfig; | ||||||
|  | import io.kamax.mxisd.lookup.ThreePidProviders; | ||||||
| 
 | 
 | ||||||
| public class ConfigurationFailureAnalyzer extends AbstractFailureAnalyzer<ConfigurationException> { | public class GoogleFirebaseStoreSupplier implements IdentityStoreSupplier { | ||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
|     protected FailureAnalysis analyze(Throwable rootFailure, ConfigurationException cause) { |     public void accept(Mxisd mxisd) { | ||||||
|         String message = cause.getMessage(); |         accept(mxisd.getConfig()); | ||||||
|         if (cause.getDetailedMessage().isPresent()) { |     } | ||||||
|             message += " - " + cause.getDetailedMessage().get(); | 
 | ||||||
|  |     public void accept(MxisdConfig cfg) { | ||||||
|  |         if (cfg.getFirebase().isEnabled()) { | ||||||
|  |             AuthProviders.register(() -> new GoogleFirebaseAuthenticator(cfg.getFirebase())); | ||||||
|  |             ThreePidProviders.register(() -> new GoogleFirebaseProvider(cfg)); | ||||||
|         } |         } | ||||||
|         return new FailureAnalysis(message, "Double check the key value", cause); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
| @@ -30,6 +30,7 @@ import io.kamax.mxisd.auth.provider.AuthenticatorProvider; | |||||||
| import io.kamax.mxisd.auth.provider.BackendAuthResult; | import io.kamax.mxisd.auth.provider.BackendAuthResult; | ||||||
| import io.kamax.mxisd.config.MatrixConfig; | import io.kamax.mxisd.config.MatrixConfig; | ||||||
| import io.kamax.mxisd.config.ldap.LdapConfig; | import io.kamax.mxisd.config.ldap.LdapConfig; | ||||||
|  | import io.kamax.mxisd.exception.InternalServerError; | ||||||
| import io.kamax.mxisd.util.GsonUtil; | import io.kamax.mxisd.util.GsonUtil; | ||||||
| import org.apache.commons.lang.StringUtils; | import org.apache.commons.lang.StringUtils; | ||||||
| import org.apache.directory.api.ldap.model.cursor.CursorException; | import org.apache.directory.api.ldap.model.cursor.CursorException; | ||||||
| @@ -42,8 +43,6 @@ import org.apache.directory.api.ldap.model.message.SearchScope; | |||||||
| import org.apache.directory.ldap.client.api.LdapConnection; | import org.apache.directory.ldap.client.api.LdapConnection; | ||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
| import org.springframework.beans.factory.annotation.Autowired; |  | ||||||
| import org.springframework.stereotype.Component; |  | ||||||
|  |  | ||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
| import java.util.HashSet; | import java.util.HashSet; | ||||||
| @@ -51,14 +50,12 @@ import java.util.List; | |||||||
| import java.util.Optional; | import java.util.Optional; | ||||||
| import java.util.Set; | import java.util.Set; | ||||||
|  |  | ||||||
| @Component |  | ||||||
| public class LdapAuthProvider extends LdapBackend implements AuthenticatorProvider { | public class LdapAuthProvider extends LdapBackend implements AuthenticatorProvider { | ||||||
|  |  | ||||||
|     private Logger log = LoggerFactory.getLogger(LdapAuthProvider.class); |     private transient final Logger log = LoggerFactory.getLogger(LdapAuthProvider.class); | ||||||
|  |  | ||||||
|     private PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance(); |     private PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance(); | ||||||
|  |  | ||||||
|     @Autowired |  | ||||||
|     public LdapAuthProvider(LdapConfig cfg, MatrixConfig mxCfg) { |     public LdapAuthProvider(LdapConfig cfg, MatrixConfig mxCfg) { | ||||||
|         super(cfg, mxCfg); |         super(cfg, mxCfg); | ||||||
|     } |     } | ||||||
| @@ -87,7 +84,6 @@ public class LdapAuthProvider extends LdapBackend implements AuthenticatorProvid | |||||||
|     public BackendAuthResult authenticate(_MatrixID mxid, String password) { |     public BackendAuthResult authenticate(_MatrixID mxid, String password) { | ||||||
|         log.info("Performing auth for {}", mxid); |         log.info("Performing auth for {}", mxid); | ||||||
|  |  | ||||||
|  |  | ||||||
|         try (LdapConnection conn = getConn()) { |         try (LdapConnection conn = getConn()) { | ||||||
|             bind(conn); |             bind(conn); | ||||||
|  |  | ||||||
| @@ -108,62 +104,65 @@ public class LdapAuthProvider extends LdapBackend implements AuthenticatorProvid | |||||||
|             String[] attArray = new String[attributes.size()]; |             String[] attArray = new String[attributes.size()]; | ||||||
|             attributes.toArray(attArray); |             attributes.toArray(attArray); | ||||||
|  |  | ||||||
|             log.debug("Base DN: {}", getBaseDn()); |  | ||||||
|             log.debug("Query: {}", userFilter); |             log.debug("Query: {}", userFilter); | ||||||
|             log.debug("Attributes: {}", GsonUtil.build().toJson(attArray)); |             log.debug("Attributes: {}", GsonUtil.build().toJson(attArray)); | ||||||
|  |  | ||||||
|             try (EntryCursor cursor = conn.search(getBaseDn(), userFilter, SearchScope.SUBTREE, attArray)) { |             for (String baseDN : getBaseDNs()) { | ||||||
|                 while (cursor.next()) { |                 log.debug("Base DN: {}", baseDN); | ||||||
|                     Entry entry = cursor.get(); |  | ||||||
|                     String dn = entry.getDn().getName(); |  | ||||||
|                     log.info("Checking possible match, DN: {}", dn); |  | ||||||
|  |  | ||||||
|                     if (!getAttribute(entry, getUidAtt()).isPresent()) { |                 try (EntryCursor cursor = conn.search(baseDN, userFilter, SearchScope.SUBTREE, attArray)) { | ||||||
|                         continue; |                     while (cursor.next()) { | ||||||
|                     } |                         Entry entry = cursor.get(); | ||||||
|  |                         String dn = entry.getDn().getName(); | ||||||
|  |                         log.info("Checking possible match, DN: {}", dn); | ||||||
|  |  | ||||||
|                     log.info("Attempting authentication on LDAP for {}", dn); |                         if (!getAttribute(entry, getUidAtt()).isPresent()) { | ||||||
|                     try { |                             continue; | ||||||
|                         conn.bind(entry.getDn(), password); |                         } | ||||||
|                     } catch (LdapException e) { |  | ||||||
|                         log.info("Unable to bind using {} because {}", entry.getDn().getName(), e.getMessage()); |  | ||||||
|                         return BackendAuthResult.failure(); |  | ||||||
|                     } |  | ||||||
|  |  | ||||||
|                     Attribute nameAttribute = entry.get(getAt().getName()); |                         log.info("Attempting authentication on LDAP for {}", dn); | ||||||
|                     String name = nameAttribute != null ? nameAttribute.get().toString() : null; |                         try { | ||||||
|  |                             conn.bind(entry.getDn(), password); | ||||||
|  |                         } catch (LdapException e) { | ||||||
|  |                             log.info("Unable to bind using {} because {}", entry.getDn().getName(), e.getMessage()); | ||||||
|  |                             return BackendAuthResult.failure(); | ||||||
|  |                         } | ||||||
|  |  | ||||||
|                     log.info("Authentication successful for {}", entry.getDn().getName()); |                         Attribute nameAttribute = entry.get(getAt().getName()); | ||||||
|                     log.info("DN {} is a valid match", dn); |                         String name = nameAttribute != null ? nameAttribute.get().toString() : null; | ||||||
|  |  | ||||||
|                     // TODO should we canonicalize the MXID? |                         log.info("Authentication successful for {}", entry.getDn().getName()); | ||||||
|                     BackendAuthResult result = BackendAuthResult.success(mxid.getId(), UserIdType.MatrixID, name); |                         log.info("DN {} is a valid match", dn); | ||||||
|                     log.info("Processing 3PIDs for profile"); |  | ||||||
|                     getAt().getThreepid().forEach((k, v) -> { |                         // TODO should we canonicalize the MXID? | ||||||
|                         log.info("Processing 3PID type {}", k); |                         BackendAuthResult result = BackendAuthResult.success(mxid.getId(), UserIdType.MatrixID, name); | ||||||
|                         v.forEach(attId -> { |                         log.info("Processing 3PIDs for profile"); | ||||||
|                             List<String> values = getAttributes(entry, attId); |                         getAt().getThreepid().forEach((k, v) -> { | ||||||
|                             log.info("\tAttribute {} has {} value(s)", attId, values.size()); |                             log.info("Processing 3PID type {}", k); | ||||||
|                             getAttributes(entry, attId).forEach(tpidValue -> { |                             v.forEach(attId -> { | ||||||
|                                 if (ThreePidMedium.PhoneNumber.is(k)) { |                                 List<String> values = getAttributes(entry, attId); | ||||||
|                                     tpidValue = getMsisdn(tpidValue).orElse(tpidValue); |                                 log.info("\tAttribute {} has {} value(s)", attId, values.size()); | ||||||
|                                 } |                                 getAttributes(entry, attId).forEach(tpidValue -> { | ||||||
|                                 result.withThreePid(new ThreePid(k, tpidValue)); |                                     if (ThreePidMedium.PhoneNumber.is(k)) { | ||||||
|  |                                         tpidValue = getMsisdn(tpidValue).orElse(tpidValue); | ||||||
|  |                                     } | ||||||
|  |                                     result.withThreePid(new ThreePid(k, tpidValue)); | ||||||
|  |                                 }); | ||||||
|                             }); |                             }); | ||||||
|                         }); |                         }); | ||||||
|                     }); |  | ||||||
|  |  | ||||||
|                     log.info("Found {} 3PIDs", result.getProfile().getThreePids().size()); |                         log.info("Found {} 3PIDs", result.getProfile().getThreePids().size()); | ||||||
|                     return result; |                         return result; | ||||||
|  |                     } | ||||||
|  |                 } catch (CursorLdapReferralException e) { | ||||||
|  |                     log.warn("Entity for {} is only available via referral, skipping", mxid); | ||||||
|                 } |                 } | ||||||
|             } catch (CursorLdapReferralException e) { |  | ||||||
|                 log.warn("Entity for {} is only available via referral, skipping", mxid); |  | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             log.info("No match were found for {}", mxid); |             log.info("No match were found for {}", mxid); | ||||||
|             return BackendAuthResult.failure(); |             return BackendAuthResult.failure(); | ||||||
|         } catch (LdapException | IOException | CursorException e) { |         } catch (LdapException | IOException | CursorException e) { | ||||||
|             throw new RuntimeException(e); |             throw new InternalServerError(e); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -45,7 +45,7 @@ public abstract class LdapBackend { | |||||||
|     public static final String UID = "uid"; |     public static final String UID = "uid"; | ||||||
|     public static final String MATRIX_ID = "mxid"; |     public static final String MATRIX_ID = "mxid"; | ||||||
|  |  | ||||||
|     private Logger log = LoggerFactory.getLogger(LdapBackend.class); |     private transient final Logger log = LoggerFactory.getLogger(LdapBackend.class); | ||||||
|  |  | ||||||
|     private LdapConfig cfg; |     private LdapConfig cfg; | ||||||
|     private MatrixConfig mxCfg; |     private MatrixConfig mxCfg; | ||||||
| @@ -59,8 +59,8 @@ public abstract class LdapBackend { | |||||||
|         return cfg; |         return cfg; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     protected String getBaseDn() { |     protected List<String> getBaseDNs() { | ||||||
|         return cfg.getConnection().getBaseDn(); |         return cfg.getConnection().getBaseDNs(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     protected LdapConfig.Attribute getAt() { |     protected LdapConfig.Attribute getAt() { | ||||||
|   | |||||||
| @@ -22,9 +22,9 @@ package io.kamax.mxisd.backend.ldap; | |||||||
|  |  | ||||||
| import io.kamax.mxisd.config.MatrixConfig; | import io.kamax.mxisd.config.MatrixConfig; | ||||||
| import io.kamax.mxisd.config.ldap.LdapConfig; | import io.kamax.mxisd.config.ldap.LdapConfig; | ||||||
| import io.kamax.mxisd.controller.directory.v1.io.UserDirectorySearchResult; | import io.kamax.mxisd.directory.DirectoryProvider; | ||||||
| import io.kamax.mxisd.directory.IDirectoryProvider; |  | ||||||
| import io.kamax.mxisd.exception.InternalServerError; | import io.kamax.mxisd.exception.InternalServerError; | ||||||
|  | import io.kamax.mxisd.http.io.UserDirectorySearchResult; | ||||||
| import io.kamax.mxisd.util.GsonUtil; | import io.kamax.mxisd.util.GsonUtil; | ||||||
| import org.apache.directory.api.ldap.model.cursor.CursorException; | import org.apache.directory.api.ldap.model.cursor.CursorException; | ||||||
| import org.apache.directory.api.ldap.model.cursor.CursorLdapReferralException; | import org.apache.directory.api.ldap.model.cursor.CursorLdapReferralException; | ||||||
| @@ -35,28 +35,19 @@ import org.apache.directory.api.ldap.model.message.SearchScope; | |||||||
| import org.apache.directory.ldap.client.api.LdapConnection; | import org.apache.directory.ldap.client.api.LdapConnection; | ||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
| import org.springframework.beans.factory.annotation.Autowired; |  | ||||||
| import org.springframework.stereotype.Component; |  | ||||||
|  |  | ||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
| import java.util.List; | import java.util.List; | ||||||
|  |  | ||||||
| @Component | public class LdapDirectoryProvider extends LdapBackend implements DirectoryProvider { | ||||||
| public class LdapDirectoryProvider extends LdapBackend implements IDirectoryProvider { |  | ||||||
|  |  | ||||||
|     private Logger log = LoggerFactory.getLogger(LdapDirectoryProvider.class); |     private transient final Logger log = LoggerFactory.getLogger(LdapDirectoryProvider.class); | ||||||
|  |  | ||||||
|     @Autowired |  | ||||||
|     public LdapDirectoryProvider(LdapConfig cfg, MatrixConfig mxCfg) { |     public LdapDirectoryProvider(LdapConfig cfg, MatrixConfig mxCfg) { | ||||||
|         super(cfg, mxCfg); |         super(cfg, mxCfg); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public boolean isEnabled() { |  | ||||||
|         return getCfg().isEnabled(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     protected UserDirectorySearchResult search(String query, List<String> attributes) { |     protected UserDirectorySearchResult search(String query, List<String> attributes) { | ||||||
|         UserDirectorySearchResult result = new UserDirectorySearchResult(); |         UserDirectorySearchResult result = new UserDirectorySearchResult(); | ||||||
|         result.setLimited(false); |         result.setLimited(false); | ||||||
| @@ -65,32 +56,34 @@ public class LdapDirectoryProvider extends LdapBackend implements IDirectoryProv | |||||||
|             bind(conn); |             bind(conn); | ||||||
|  |  | ||||||
|             LdapConfig.Attribute atCfg = getCfg().getAttribute(); |             LdapConfig.Attribute atCfg = getCfg().getAttribute(); | ||||||
|  |  | ||||||
|             attributes = new ArrayList<>(attributes); |             attributes = new ArrayList<>(attributes); | ||||||
|             attributes.add(getUidAtt()); |             attributes.add(getUidAtt()); | ||||||
|             String[] attArray = new String[attributes.size()]; |             String[] attArray = new String[attributes.size()]; | ||||||
|             attributes.toArray(attArray); |             attributes.toArray(attArray); | ||||||
|             String searchQuery = buildOrQueryWithFilter(getCfg().getDirectory().getFilter(), "*" + query + "*", attArray); |             String searchQuery = buildOrQueryWithFilter(getCfg().getDirectory().getFilter(), "*" + query + "*", attArray); | ||||||
|  |  | ||||||
|             log.debug("Base DN: {}", getBaseDn()); |  | ||||||
|             log.debug("Query: {}", searchQuery); |             log.debug("Query: {}", searchQuery); | ||||||
|             log.debug("Attributes: {}", GsonUtil.build().toJson(attArray)); |             log.debug("Attributes: {}", GsonUtil.build().toJson(attArray)); | ||||||
|  |  | ||||||
|             try (EntryCursor cursor = conn.search(getBaseDn(), searchQuery, SearchScope.SUBTREE, attArray)) { |             for (String baseDN : getBaseDNs()) { | ||||||
|                 while (cursor.next()) { |                 log.debug("Base DN: {}", baseDN); | ||||||
|                     Entry entry = cursor.get(); |  | ||||||
|                     log.info("Found possible match, DN: {}", entry.getDn().getName()); |                 try (EntryCursor cursor = conn.search(baseDN, searchQuery, SearchScope.SUBTREE, attArray)) { | ||||||
|                     getAttribute(entry, getUidAtt()).ifPresent(uid -> { |                     while (cursor.next()) { | ||||||
|                         log.info("DN {} is a valid match", entry.getDn().getName()); |                         Entry entry = cursor.get(); | ||||||
|                         try { |                         log.info("Found possible match, DN: {}", entry.getDn().getName()); | ||||||
|                             UserDirectorySearchResult.Result entryResult = new UserDirectorySearchResult.Result(); |                         getAttribute(entry, getUidAtt()).ifPresent(uid -> { | ||||||
|                             entryResult.setUserId(buildMatrixIdFromUid(uid)); |                             log.info("DN {} is a valid match", entry.getDn().getName()); | ||||||
|                             getAttribute(entry, atCfg.getName()).ifPresent(entryResult::setDisplayName); |                             try { | ||||||
|                             result.addResult(entryResult); |                                 UserDirectorySearchResult.Result entryResult = new UserDirectorySearchResult.Result(); | ||||||
|                         } catch (IllegalArgumentException e) { |                                 entryResult.setUserId(buildMatrixIdFromUid(uid)); | ||||||
|                             log.warn("Bind was found but type {} is not supported", atCfg.getUid().getType()); |                                 getAttribute(entry, atCfg.getName()).ifPresent(entryResult::setDisplayName); | ||||||
|                         } |                                 result.addResult(entryResult); | ||||||
|                     }); |                             } catch (IllegalArgumentException e) { | ||||||
|  |                                 log.warn("Bind was found but type {} is not supported", atCfg.getUid().getType()); | ||||||
|  |                             } | ||||||
|  |                         }); | ||||||
|  |                     } | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } catch (CursorLdapReferralException e) { |         } catch (CursorLdapReferralException e) { | ||||||
|   | |||||||
| @@ -36,8 +36,6 @@ import org.apache.directory.api.ldap.model.message.SearchScope; | |||||||
| import org.apache.directory.ldap.client.api.LdapConnection; | import org.apache.directory.ldap.client.api.LdapConnection; | ||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
| import org.springframework.beans.factory.annotation.Autowired; |  | ||||||
| import org.springframework.stereotype.Component; |  | ||||||
|  |  | ||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
| @@ -45,21 +43,14 @@ import java.util.Collections; | |||||||
| import java.util.List; | import java.util.List; | ||||||
| import java.util.Optional; | import java.util.Optional; | ||||||
|  |  | ||||||
| @Component |  | ||||||
| public class LdapProfileProvider extends LdapBackend implements ProfileProvider { | public class LdapProfileProvider extends LdapBackend implements ProfileProvider { | ||||||
|  |  | ||||||
|     private transient Logger log = LoggerFactory.getLogger(LdapProfileProvider.class); |     private transient final Logger log = LoggerFactory.getLogger(LdapProfileProvider.class); | ||||||
|  |  | ||||||
|     @Autowired |  | ||||||
|     public LdapProfileProvider(LdapConfig cfg, MatrixConfig mxCfg) { |     public LdapProfileProvider(LdapConfig cfg, MatrixConfig mxCfg) { | ||||||
|         super(cfg, mxCfg); |         super(cfg, mxCfg); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public boolean isEnabled() { |  | ||||||
|         return getCfg().isEnabled(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public Optional<String> getDisplayName(_MatrixID userId) { |     public Optional<String> getDisplayName(_MatrixID userId) { | ||||||
|         String uid = buildUidFromMatrixId(userId); |         String uid = buildUidFromMatrixId(userId); | ||||||
| @@ -69,32 +60,33 @@ public class LdapProfileProvider extends LdapBackend implements ProfileProvider | |||||||
|             bind(conn); |             bind(conn); | ||||||
|  |  | ||||||
|             String searchQuery = buildOrQueryWithFilter(getCfg().getProfile().getFilter(), uid, getUidAtt()); |             String searchQuery = buildOrQueryWithFilter(getCfg().getProfile().getFilter(), uid, getUidAtt()); | ||||||
|  |  | ||||||
|             log.debug("Base DN: {}", getBaseDn()); |  | ||||||
|             log.debug("Query: {}", searchQuery); |             log.debug("Query: {}", searchQuery); | ||||||
|  |  | ||||||
|             try (EntryCursor cursor = conn.search(getBaseDn(), searchQuery, SearchScope.SUBTREE, getAt().getName())) { |             for (String baseDN : getBaseDNs()) { | ||||||
|                 while (cursor.next()) { |                 log.debug("Base DN: {}", baseDN); | ||||||
|                     Entry entry = cursor.get(); |                 try (EntryCursor cursor = conn.search(baseDN, searchQuery, SearchScope.SUBTREE, getAt().getName())) { | ||||||
|                     log.info("Found possible match, DN: {}", entry.getDn().getName()); |                     while (cursor.next()) { | ||||||
|                     Optional<String> v = getAttribute(entry, getAt().getName()).flatMap(id -> { |                         Entry entry = cursor.get(); | ||||||
|                         log.info("DN {} is a valid match", entry.getDn().getName()); |                         log.info("Found possible match, DN: {}", entry.getDn().getName()); | ||||||
|                         try { |                         Optional<String> v = getAttribute(entry, getAt().getName()).flatMap(id -> { | ||||||
|                             return getAttribute(entry, getAt().getName()); |                             log.info("DN {} is a valid match", entry.getDn().getName()); | ||||||
|                         } catch (IllegalArgumentException e) { |                             try { | ||||||
|                             log.warn("Bind was found but type {} is not supported", getAt().getUid().getType()); |                                 return getAttribute(entry, getAt().getName()); | ||||||
|                             return Optional.empty(); |                             } catch (IllegalArgumentException e) { | ||||||
|                         } |                                 log.warn("Bind was found but type {} is not supported", getAt().getUid().getType()); | ||||||
|                     }); |                                 return Optional.empty(); | ||||||
|  |                             } | ||||||
|  |                         }); | ||||||
|  |  | ||||||
|                     if (v.isPresent()) { |                         if (v.isPresent()) { | ||||||
|                         log.info("DN {} is the final match", entry.getDn().getName()); |                             log.info("DN {} is the final match", entry.getDn().getName()); | ||||||
|                         return v; |                             return v; | ||||||
|  |                         } | ||||||
|                     } |                     } | ||||||
|  |                 } catch (CursorLdapReferralException e) { | ||||||
|  |                     log.warn("An entry is only available via referral, skipping"); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } catch (CursorLdapReferralException e) { |  | ||||||
|             log.warn("An entry is only available via referral, skipping"); |  | ||||||
|         } catch (IOException | LdapException | CursorException e) { |         } catch (IOException | LdapException | CursorException e) { | ||||||
|             throw new InternalServerError(e); |             throw new InternalServerError(e); | ||||||
|         } |         } | ||||||
| @@ -111,7 +103,6 @@ public class LdapProfileProvider extends LdapBackend implements ProfileProvider | |||||||
|         try (LdapConnection conn = getConn()) { |         try (LdapConnection conn = getConn()) { | ||||||
|             bind(conn); |             bind(conn); | ||||||
|  |  | ||||||
|             log.debug("Base DN: {}", getBaseDn()); |  | ||||||
|             getCfg().getAttribute().getThreepid().forEach((medium, attributes) -> { |             getCfg().getAttribute().getThreepid().forEach((medium, attributes) -> { | ||||||
|                 String[] attArray = new String[attributes.size()]; |                 String[] attArray = new String[attributes.size()]; | ||||||
|                 attributes.toArray(attArray); |                 attributes.toArray(attArray); | ||||||
| @@ -120,28 +111,30 @@ public class LdapProfileProvider extends LdapBackend implements ProfileProvider | |||||||
|  |  | ||||||
|                 log.debug("Query for 3PID {}: {}", medium, searchQuery); |                 log.debug("Query for 3PID {}: {}", medium, searchQuery); | ||||||
|  |  | ||||||
|                 try (EntryCursor cursor = conn.search(getBaseDn(), searchQuery, SearchScope.SUBTREE, attArray)) { |                 for (String baseDN : getBaseDNs()) { | ||||||
|                     while (cursor.next()) { |                     log.debug("Base DN: {}", baseDN); | ||||||
|                         Entry entry = cursor.get(); |                     try (EntryCursor cursor = conn.search(baseDN, searchQuery, SearchScope.SUBTREE, attArray)) { | ||||||
|                         log.info("Found possible match, DN: {}", entry.getDn().getName()); |                         while (cursor.next()) { | ||||||
|                         try { |                             Entry entry = cursor.get(); | ||||||
|                             attributes.stream() |                             log.info("Found possible match, DN: {}", entry.getDn().getName()); | ||||||
|                                     .flatMap(at -> getAttributes(entry, at).stream()) |                             try { | ||||||
|                                     .forEach(address -> { |                                 attributes.stream() | ||||||
|                                         log.info("Found 3PID: {} - {}", medium, address); |                                         .flatMap(at -> getAttributes(entry, at).stream()) | ||||||
|                                         threePids.add(new ThreePid(medium, address)); |                                         .forEach(address -> { | ||||||
|                                     }); |                                             log.info("Found 3PID: {} - {}", medium, address); | ||||||
|                         } catch (IllegalArgumentException e) { |                                             threePids.add(new ThreePid(medium, address)); | ||||||
|                             log.warn("Bind was found but type {} is not supported", getAt().getUid().getType()); |                                         }); | ||||||
|  |                             } catch (IllegalArgumentException e) { | ||||||
|  |                                 log.warn("Bind was found but type {} is not supported", getAt().getUid().getType()); | ||||||
|  |                             } | ||||||
|                         } |                         } | ||||||
|  |                     } catch (CursorLdapReferralException e) { | ||||||
|  |                         log.warn("An entry is only available via referral, skipping"); | ||||||
|  |                     } catch (LdapException | IOException | CursorException e) { | ||||||
|  |                         throw new InternalServerError(e); | ||||||
|                     } |                     } | ||||||
|                 } catch (CursorLdapReferralException e) { |  | ||||||
|                     log.warn("An entry is only available via referral, skipping"); |  | ||||||
|                 } catch (IOException | LdapException | CursorException e) { |  | ||||||
|                     throw new InternalServerError(e); |  | ||||||
|                 } |                 } | ||||||
|             }); |             }); | ||||||
|  |  | ||||||
|         } catch (IOException | LdapException e) { |         } catch (IOException | LdapException e) { | ||||||
|             throw new InternalServerError(e); |             throw new InternalServerError(e); | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -0,0 +1,47 @@ | |||||||
|  | /* | ||||||
|  |  * mxisd - Matrix Identity Server Daemon | ||||||
|  |  * 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.mxisd.backend.ldap; | ||||||
|  |  | ||||||
|  | import io.kamax.mxisd.Mxisd; | ||||||
|  | import io.kamax.mxisd.auth.AuthProviders; | ||||||
|  | import io.kamax.mxisd.backend.IdentityStoreSupplier; | ||||||
|  | import io.kamax.mxisd.config.MxisdConfig; | ||||||
|  | import io.kamax.mxisd.directory.DirectoryProviders; | ||||||
|  | import io.kamax.mxisd.lookup.ThreePidProviders; | ||||||
|  | import io.kamax.mxisd.profile.ProfileProviders; | ||||||
|  |  | ||||||
|  | public class LdapStoreSupplier implements IdentityStoreSupplier { | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void accept(Mxisd mxisd) { | ||||||
|  |         accept(mxisd.getConfig()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void accept(MxisdConfig cfg) { | ||||||
|  |         if (cfg.getLdap().isEnabled()) { | ||||||
|  |             AuthProviders.register(() -> new LdapAuthProvider(cfg.getLdap(), cfg.getMatrix())); | ||||||
|  |             DirectoryProviders.register(() -> new LdapDirectoryProvider(cfg.getLdap(), cfg.getMatrix())); | ||||||
|  |             ThreePidProviders.register(() -> new LdapThreePidProvider(cfg.getLdap(), cfg.getMatrix())); | ||||||
|  |             ProfileProviders.register(() -> new LdapProfileProvider(cfg.getLdap(), cfg.getMatrix())); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -37,27 +37,20 @@ import org.apache.directory.api.ldap.model.message.SearchScope; | |||||||
| import org.apache.directory.ldap.client.api.LdapConnection; | import org.apache.directory.ldap.client.api.LdapConnection; | ||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
| import org.springframework.stereotype.Component; |  | ||||||
|  |  | ||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
| import java.util.List; | import java.util.List; | ||||||
| import java.util.Optional; | import java.util.Optional; | ||||||
|  |  | ||||||
| @Component |  | ||||||
| public class LdapThreePidProvider extends LdapBackend implements IThreePidProvider { | public class LdapThreePidProvider extends LdapBackend implements IThreePidProvider { | ||||||
|  |  | ||||||
|     private Logger log = LoggerFactory.getLogger(LdapThreePidProvider.class); |     private transient final Logger log = LoggerFactory.getLogger(LdapThreePidProvider.class); | ||||||
|  |  | ||||||
|     public LdapThreePidProvider(LdapConfig cfg, MatrixConfig mxCfg) { |     public LdapThreePidProvider(LdapConfig cfg, MatrixConfig mxCfg) { | ||||||
|         super(cfg, mxCfg); |         super(cfg, mxCfg); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public boolean isEnabled() { |  | ||||||
|         return getCfg().isEnabled(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public boolean isLocal() { |     public boolean isLocal() { | ||||||
|         return true; |         return true; | ||||||
| @@ -78,28 +71,30 @@ public class LdapThreePidProvider extends LdapBackend implements IThreePidProvid | |||||||
|         // we merge 3PID specific query with global/specific filter, if one exists. |         // we merge 3PID specific query with global/specific filter, if one exists. | ||||||
|         String tPidQuery = tPidQueryOpt.get().replaceAll(getCfg().getIdentity().getToken(), value); |         String tPidQuery = tPidQueryOpt.get().replaceAll(getCfg().getIdentity().getToken(), value); | ||||||
|         String searchQuery = buildWithFilter(tPidQuery, getCfg().getIdentity().getFilter()); |         String searchQuery = buildWithFilter(tPidQuery, getCfg().getIdentity().getFilter()); | ||||||
|  |  | ||||||
|         log.debug("Base DN: {}", getBaseDn()); |  | ||||||
|         log.debug("Query: {}", searchQuery); |         log.debug("Query: {}", searchQuery); | ||||||
|         log.debug("Attributes: {}", GsonUtil.build().toJson(getUidAtt())); |         log.debug("Attributes: {}", GsonUtil.build().toJson(getUidAtt())); | ||||||
|  |  | ||||||
|         try (EntryCursor cursor = conn.search(getBaseDn(), searchQuery, SearchScope.SUBTREE, getUidAtt())) { |         for (String baseDN : getBaseDNs()) { | ||||||
|             while (cursor.next()) { |             log.debug("Base DN: {}", baseDN); | ||||||
|                 Entry entry = cursor.get(); |  | ||||||
|                 log.info("Found possible match, DN: {}", entry.getDn().getName()); |  | ||||||
|  |  | ||||||
|                 Optional<String> data = getAttribute(entry, getUidAtt()); |             try (EntryCursor cursor = conn.search(baseDN, searchQuery, SearchScope.SUBTREE, getUidAtt())) { | ||||||
|                 if (!data.isPresent()) { |                 while (cursor.next()) { | ||||||
|                     continue; |                     Entry entry = cursor.get(); | ||||||
|  |                     log.info("Found possible match, DN: {}", entry.getDn().getName()); | ||||||
|  |  | ||||||
|  |                     Optional<String> data = getAttribute(entry, getUidAtt()); | ||||||
|  |                     if (!data.isPresent()) { | ||||||
|  |                         continue; | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                     log.info("DN {} is a valid match", entry.getDn().getName()); | ||||||
|  |                     return Optional.of(buildMatrixIdFromUid(data.get())); | ||||||
|                 } |                 } | ||||||
|  |             } catch (CursorLdapReferralException e) { | ||||||
|                 log.info("DN {} is a valid match", entry.getDn().getName()); |                 log.warn("3PID {} is only available via referral, skipping", value); | ||||||
|                 return Optional.of(buildMatrixIdFromUid(data.get())); |             } catch (IOException | LdapException | CursorException e) { | ||||||
|  |                 throw new InternalServerError(e); | ||||||
|             } |             } | ||||||
|         } catch (CursorLdapReferralException e) { |  | ||||||
|             log.warn("3PID {} is only available via referral, skipping", value); |  | ||||||
|         } catch (IOException | LdapException | CursorException e) { |  | ||||||
|             throw new InternalServerError(e); |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         return Optional.empty(); |         return Optional.empty(); | ||||||
|   | |||||||
| @@ -23,9 +23,7 @@ package io.kamax.mxisd.backend.ldap.netiq; | |||||||
| import io.kamax.mxisd.backend.ldap.LdapAuthProvider; | import io.kamax.mxisd.backend.ldap.LdapAuthProvider; | ||||||
| import io.kamax.mxisd.config.MatrixConfig; | import io.kamax.mxisd.config.MatrixConfig; | ||||||
| import io.kamax.mxisd.config.ldap.netiq.NetIqLdapConfig; | import io.kamax.mxisd.config.ldap.netiq.NetIqLdapConfig; | ||||||
| import org.springframework.stereotype.Component; |  | ||||||
|  |  | ||||||
| @Component |  | ||||||
| public class NetIqLdapAuthProvider extends LdapAuthProvider { | public class NetIqLdapAuthProvider extends LdapAuthProvider { | ||||||
|  |  | ||||||
|     public NetIqLdapAuthProvider(NetIqLdapConfig cfg, MatrixConfig mxCfg) { |     public NetIqLdapAuthProvider(NetIqLdapConfig cfg, MatrixConfig mxCfg) { | ||||||
|   | |||||||
| @@ -23,9 +23,7 @@ package io.kamax.mxisd.backend.ldap.netiq; | |||||||
| import io.kamax.mxisd.backend.ldap.LdapDirectoryProvider; | import io.kamax.mxisd.backend.ldap.LdapDirectoryProvider; | ||||||
| import io.kamax.mxisd.config.MatrixConfig; | import io.kamax.mxisd.config.MatrixConfig; | ||||||
| import io.kamax.mxisd.config.ldap.netiq.NetIqLdapConfig; | import io.kamax.mxisd.config.ldap.netiq.NetIqLdapConfig; | ||||||
| import org.springframework.stereotype.Component; |  | ||||||
|  |  | ||||||
| @Component |  | ||||||
| public class NetIqLdapDirectoryProvider extends LdapDirectoryProvider { | public class NetIqLdapDirectoryProvider extends LdapDirectoryProvider { | ||||||
|  |  | ||||||
|     public NetIqLdapDirectoryProvider(NetIqLdapConfig cfg, MatrixConfig mxCfg) { |     public NetIqLdapDirectoryProvider(NetIqLdapConfig cfg, MatrixConfig mxCfg) { | ||||||
|   | |||||||
| @@ -24,13 +24,9 @@ import io.kamax.matrix._MatrixID; | |||||||
| import io.kamax.mxisd.backend.ldap.LdapProfileProvider; | import io.kamax.mxisd.backend.ldap.LdapProfileProvider; | ||||||
| import io.kamax.mxisd.config.MatrixConfig; | import io.kamax.mxisd.config.MatrixConfig; | ||||||
| import io.kamax.mxisd.config.ldap.netiq.NetIqLdapConfig; | import io.kamax.mxisd.config.ldap.netiq.NetIqLdapConfig; | ||||||
| import org.springframework.beans.factory.annotation.Autowired; |  | ||||||
| import org.springframework.stereotype.Component; |  | ||||||
|  |  | ||||||
| @Component |  | ||||||
| public class NetIqLdapProfileProvider extends LdapProfileProvider { | public class NetIqLdapProfileProvider extends LdapProfileProvider { | ||||||
|  |  | ||||||
|     @Autowired |  | ||||||
|     public NetIqLdapProfileProvider(NetIqLdapConfig cfg, MatrixConfig mxCfg) { |     public NetIqLdapProfileProvider(NetIqLdapConfig cfg, MatrixConfig mxCfg) { | ||||||
|         super(cfg, mxCfg); |         super(cfg, mxCfg); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -0,0 +1,47 @@ | |||||||
|  | /* | ||||||
|  |  * mxisd - Matrix Identity Server Daemon | ||||||
|  |  * 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.mxisd.backend.ldap.netiq; | ||||||
|  |  | ||||||
|  | import io.kamax.mxisd.Mxisd; | ||||||
|  | import io.kamax.mxisd.auth.AuthProviders; | ||||||
|  | import io.kamax.mxisd.backend.IdentityStoreSupplier; | ||||||
|  | import io.kamax.mxisd.config.MxisdConfig; | ||||||
|  | import io.kamax.mxisd.directory.DirectoryProviders; | ||||||
|  | import io.kamax.mxisd.lookup.ThreePidProviders; | ||||||
|  | import io.kamax.mxisd.profile.ProfileProviders; | ||||||
|  |  | ||||||
|  | public class NetIqLdapStoreSupplier implements IdentityStoreSupplier { | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void accept(Mxisd mxisd) { | ||||||
|  |         accept(mxisd.getConfig()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void accept(MxisdConfig cfg) { | ||||||
|  |         if (cfg.getNetiq().isEnabled()) { | ||||||
|  |             AuthProviders.register(() -> new NetIqLdapAuthProvider(cfg.getNetiq(), cfg.getMatrix())); | ||||||
|  |             DirectoryProviders.register(() -> new NetIqLdapDirectoryProvider(cfg.getNetiq(), cfg.getMatrix())); | ||||||
|  |             ThreePidProviders.register(() -> new NetIqLdapThreePidProvider(cfg.getNetiq(), cfg.getMatrix())); | ||||||
|  |             ProfileProviders.register(() -> new NetIqLdapProfileProvider(cfg.getNetiq(), cfg.getMatrix())); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -23,9 +23,7 @@ package io.kamax.mxisd.backend.ldap.netiq; | |||||||
| import io.kamax.mxisd.backend.ldap.LdapThreePidProvider; | import io.kamax.mxisd.backend.ldap.LdapThreePidProvider; | ||||||
| import io.kamax.mxisd.config.MatrixConfig; | import io.kamax.mxisd.config.MatrixConfig; | ||||||
| import io.kamax.mxisd.config.ldap.netiq.NetIqLdapConfig; | import io.kamax.mxisd.config.ldap.netiq.NetIqLdapConfig; | ||||||
| import org.springframework.stereotype.Component; |  | ||||||
|  |  | ||||||
| @Component |  | ||||||
| public class NetIqLdapThreePidProvider extends LdapThreePidProvider { | public class NetIqLdapThreePidProvider extends LdapThreePidProvider { | ||||||
|  |  | ||||||
|     public NetIqLdapThreePidProvider(NetIqLdapConfig cfg, MatrixConfig mxCfg) { |     public NetIqLdapThreePidProvider(NetIqLdapConfig cfg, MatrixConfig mxCfg) { | ||||||
|   | |||||||
| @@ -31,8 +31,8 @@ import io.kamax.mxisd.config.MatrixConfig; | |||||||
| import io.kamax.mxisd.config.memory.MemoryIdentityConfig; | import io.kamax.mxisd.config.memory.MemoryIdentityConfig; | ||||||
| import io.kamax.mxisd.config.memory.MemoryStoreConfig; | import io.kamax.mxisd.config.memory.MemoryStoreConfig; | ||||||
| import io.kamax.mxisd.config.memory.MemoryThreePid; | import io.kamax.mxisd.config.memory.MemoryThreePid; | ||||||
| import io.kamax.mxisd.controller.directory.v1.io.UserDirectorySearchResult; | import io.kamax.mxisd.directory.DirectoryProvider; | ||||||
| import io.kamax.mxisd.directory.IDirectoryProvider; | import io.kamax.mxisd.http.io.UserDirectorySearchResult; | ||||||
| import io.kamax.mxisd.lookup.SingleLookupReply; | import io.kamax.mxisd.lookup.SingleLookupReply; | ||||||
| import io.kamax.mxisd.lookup.SingleLookupRequest; | import io.kamax.mxisd.lookup.SingleLookupRequest; | ||||||
| import io.kamax.mxisd.lookup.ThreePidMapping; | import io.kamax.mxisd.lookup.ThreePidMapping; | ||||||
| @@ -41,8 +41,6 @@ import io.kamax.mxisd.profile.ProfileProvider; | |||||||
| import org.apache.commons.lang.StringUtils; | import org.apache.commons.lang.StringUtils; | ||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
| import org.springframework.beans.factory.annotation.Autowired; |  | ||||||
| import org.springframework.stereotype.Component; |  | ||||||
|  |  | ||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
| import java.util.Collections; | import java.util.Collections; | ||||||
| @@ -51,15 +49,13 @@ import java.util.Optional; | |||||||
| import java.util.function.Function; | import java.util.function.Function; | ||||||
| import java.util.function.Predicate; | import java.util.function.Predicate; | ||||||
|  |  | ||||||
| @Component | public class MemoryIdentityStore implements AuthenticatorProvider, DirectoryProvider, IThreePidProvider, ProfileProvider { | ||||||
| public class MemoryIdentityStore implements AuthenticatorProvider, IDirectoryProvider, IThreePidProvider, ProfileProvider { |  | ||||||
|  |  | ||||||
|     private final Logger logger = LoggerFactory.getLogger(MemoryIdentityStore.class); |     private transient final Logger logger = LoggerFactory.getLogger(MemoryIdentityStore.class); | ||||||
|  |  | ||||||
|     private final MatrixConfig mxCfg; |     private final MatrixConfig mxCfg; | ||||||
|     private final MemoryStoreConfig cfg; |     private final MemoryStoreConfig cfg; | ||||||
|  |  | ||||||
|     @Autowired |  | ||||||
|     public MemoryIdentityStore(MatrixConfig mxCfg, MemoryStoreConfig cfg) { |     public MemoryIdentityStore(MatrixConfig mxCfg, MemoryStoreConfig cfg) { | ||||||
|         this.mxCfg = mxCfg; |         this.mxCfg = mxCfg; | ||||||
|         this.cfg = cfg; |         this.cfg = cfg; | ||||||
|   | |||||||
| @@ -0,0 +1,51 @@ | |||||||
|  | /* | ||||||
|  |  * mxisd - Matrix Identity Server Daemon | ||||||
|  |  * 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.mxisd.backend.memory; | ||||||
|  |  | ||||||
|  | import io.kamax.mxisd.Mxisd; | ||||||
|  | import io.kamax.mxisd.auth.AuthProviders; | ||||||
|  | import io.kamax.mxisd.backend.IdentityStoreSupplier; | ||||||
|  | import io.kamax.mxisd.config.MxisdConfig; | ||||||
|  | import io.kamax.mxisd.directory.DirectoryProviders; | ||||||
|  | import io.kamax.mxisd.lookup.ThreePidProviders; | ||||||
|  | import io.kamax.mxisd.profile.ProfileProviders; | ||||||
|  |  | ||||||
|  | import java.util.function.Supplier; | ||||||
|  |  | ||||||
|  | public class MemoryIdentityStoreSupplier implements IdentityStoreSupplier { | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void accept(Mxisd mxisd) { | ||||||
|  |         accept(mxisd.getConfig()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void accept(MxisdConfig cfg) { | ||||||
|  |         if (cfg.getMemory().isEnabled()) { | ||||||
|  |             Supplier<MemoryIdentityStore> supplier = () -> new MemoryIdentityStore(cfg.getMatrix(), cfg.getMemory()); | ||||||
|  |  | ||||||
|  |             AuthProviders.register(supplier); | ||||||
|  |             DirectoryProviders.register(supplier); | ||||||
|  |             ThreePidProviders.register(supplier); | ||||||
|  |             ProfileProviders.register(supplier); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -27,15 +27,11 @@ import io.kamax.mxisd.config.rest.RestBackendConfig; | |||||||
| import io.kamax.mxisd.util.RestClientUtils; | import io.kamax.mxisd.util.RestClientUtils; | ||||||
| import org.apache.http.client.methods.CloseableHttpResponse; | import org.apache.http.client.methods.CloseableHttpResponse; | ||||||
| import org.apache.http.client.methods.HttpUriRequest; | import org.apache.http.client.methods.HttpUriRequest; | ||||||
| import org.springframework.beans.factory.annotation.Autowired; |  | ||||||
| import org.springframework.stereotype.Component; |  | ||||||
|  |  | ||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
|  |  | ||||||
| @Component |  | ||||||
| public class RestAuthProvider extends RestProvider implements AuthenticatorProvider { | public class RestAuthProvider extends RestProvider implements AuthenticatorProvider { | ||||||
|  |  | ||||||
|     @Autowired |  | ||||||
|     public RestAuthProvider(RestBackendConfig cfg) { |     public RestAuthProvider(RestBackendConfig cfg) { | ||||||
|         super(cfg); |         super(cfg); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -23,21 +23,18 @@ package io.kamax.mxisd.backend.rest; | |||||||
| import io.kamax.matrix.MatrixID; | import io.kamax.matrix.MatrixID; | ||||||
| import io.kamax.mxisd.config.MatrixConfig; | import io.kamax.mxisd.config.MatrixConfig; | ||||||
| import io.kamax.mxisd.config.rest.RestBackendConfig; | import io.kamax.mxisd.config.rest.RestBackendConfig; | ||||||
| import io.kamax.mxisd.controller.directory.v1.io.UserDirectorySearchRequest; | import io.kamax.mxisd.directory.DirectoryProvider; | ||||||
| import io.kamax.mxisd.controller.directory.v1.io.UserDirectorySearchResult; |  | ||||||
| import io.kamax.mxisd.directory.IDirectoryProvider; |  | ||||||
| import io.kamax.mxisd.exception.InternalServerError; | import io.kamax.mxisd.exception.InternalServerError; | ||||||
|  | import io.kamax.mxisd.http.io.UserDirectorySearchRequest; | ||||||
|  | import io.kamax.mxisd.http.io.UserDirectorySearchResult; | ||||||
| import io.kamax.mxisd.util.RestClientUtils; | import io.kamax.mxisd.util.RestClientUtils; | ||||||
| import org.apache.commons.io.IOUtils; | import org.apache.commons.io.IOUtils; | ||||||
| import org.apache.commons.lang.StringUtils; |  | ||||||
| import org.apache.http.client.methods.CloseableHttpResponse; | import org.apache.http.client.methods.CloseableHttpResponse; | ||||||
| import org.springframework.stereotype.Component; |  | ||||||
|  |  | ||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
| import java.nio.charset.StandardCharsets; | import java.nio.charset.StandardCharsets; | ||||||
|  |  | ||||||
| @Component | public class RestDirectoryProvider extends RestProvider implements DirectoryProvider { | ||||||
| public class RestDirectoryProvider extends RestProvider implements IDirectoryProvider { |  | ||||||
|  |  | ||||||
|     private MatrixConfig mxCfg; |     private MatrixConfig mxCfg; | ||||||
|  |  | ||||||
| @@ -46,11 +43,6 @@ public class RestDirectoryProvider extends RestProvider implements IDirectoryPro | |||||||
|         this.mxCfg = mxCfg; |         this.mxCfg = mxCfg; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public boolean isEnabled() { |  | ||||||
|         return cfg.isEnabled() && StringUtils.isNotBlank(cfg.getEndpoints().getDirectory()); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private UserDirectorySearchResult search(String by, String query) { |     private UserDirectorySearchResult search(String by, String query) { | ||||||
|         UserDirectorySearchRequest request = new UserDirectorySearchRequest(query); |         UserDirectorySearchRequest request = new UserDirectorySearchRequest(query); | ||||||
|         request.setBy(by); |         request.setBy(by); | ||||||
|   | |||||||
| @@ -0,0 +1,140 @@ | |||||||
|  | /* | ||||||
|  |  * mxisd - Matrix Identity Server Daemon | ||||||
|  |  * 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.mxisd.backend.rest; | ||||||
|  |  | ||||||
|  | import com.google.gson.JsonObject; | ||||||
|  | import com.google.gson.JsonSyntaxException; | ||||||
|  | import io.kamax.matrix._MatrixID; | ||||||
|  | import io.kamax.matrix._ThreePid; | ||||||
|  | import io.kamax.matrix.json.GsonUtil; | ||||||
|  | import io.kamax.matrix.json.InvalidJsonException; | ||||||
|  | import io.kamax.mxisd.config.rest.RestBackendConfig; | ||||||
|  | import io.kamax.mxisd.exception.InternalServerError; | ||||||
|  | import io.kamax.mxisd.profile.JsonProfileRequest; | ||||||
|  | import io.kamax.mxisd.profile.JsonProfileResult; | ||||||
|  | import io.kamax.mxisd.profile.ProfileProvider; | ||||||
|  | import org.apache.commons.io.IOUtils; | ||||||
|  | import org.apache.commons.lang.StringUtils; | ||||||
|  | import org.apache.http.client.methods.CloseableHttpResponse; | ||||||
|  | import org.apache.http.client.methods.HttpPost; | ||||||
|  | import org.apache.http.client.utils.URIBuilder; | ||||||
|  | import org.apache.http.entity.ContentType; | ||||||
|  | import org.apache.http.entity.StringEntity; | ||||||
|  | import org.slf4j.Logger; | ||||||
|  | import org.slf4j.LoggerFactory; | ||||||
|  |  | ||||||
|  | import java.io.IOException; | ||||||
|  | import java.net.URISyntaxException; | ||||||
|  | import java.nio.charset.StandardCharsets; | ||||||
|  | import java.util.*; | ||||||
|  | import java.util.function.Function; | ||||||
|  |  | ||||||
|  | public class RestProfileProvider extends RestProvider implements ProfileProvider { | ||||||
|  |  | ||||||
|  |     private transient final Logger log = LoggerFactory.getLogger(RestProfileProvider.class); | ||||||
|  |  | ||||||
|  |     public RestProfileProvider(RestBackendConfig cfg) { | ||||||
|  |         super(cfg); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private <T> Optional<T> doRequest( | ||||||
|  |             _MatrixID userId, | ||||||
|  |             Function<RestBackendConfig.ProfileEndpoints, Optional<String>> endpoint, | ||||||
|  |             Function<JsonProfileResult, Optional<T>> value | ||||||
|  |     ) { | ||||||
|  |         return cfg.getEndpoints().getProfile() | ||||||
|  |                 // We get the endpoint | ||||||
|  |                 .flatMap(endpoint) | ||||||
|  |                 // We only continue if there is a value | ||||||
|  |                 .filter(StringUtils::isNotBlank) | ||||||
|  |                 // We use the endpoint | ||||||
|  |                 .flatMap(url -> { | ||||||
|  |                     try { | ||||||
|  |                         URIBuilder builder = new URIBuilder(url); | ||||||
|  |                         HttpPost req = new HttpPost(builder.build()); | ||||||
|  |                         req.setEntity(new StringEntity(GsonUtil.get().toJson(new JsonProfileRequest(userId)), ContentType.APPLICATION_JSON)); | ||||||
|  |                         try (CloseableHttpResponse res = client.execute(req)) { | ||||||
|  |                             int sc = res.getStatusLine().getStatusCode(); | ||||||
|  |                             if (sc == 404) { | ||||||
|  |                                 log.info("Got 404 - No result found"); | ||||||
|  |                                 return Optional.empty(); | ||||||
|  |                             } | ||||||
|  |  | ||||||
|  |                             if (sc != 200) { | ||||||
|  |                                 throw new InternalServerError("Unexpected backed status code: " + sc); | ||||||
|  |                             } | ||||||
|  |  | ||||||
|  |                             String body = IOUtils.toString(res.getEntity().getContent(), StandardCharsets.UTF_8); | ||||||
|  |                             if (StringUtils.isBlank(body)) { | ||||||
|  |                                 log.warn("Backend response body is empty/blank, expected JSON object with profile key"); | ||||||
|  |                                 return Optional.empty(); | ||||||
|  |                             } | ||||||
|  |  | ||||||
|  |                             Optional<JsonObject> pJson = GsonUtil.findObj(GsonUtil.parseObj(body), "profile"); | ||||||
|  |                             if (!pJson.isPresent()) { | ||||||
|  |                                 log.warn("Backend response body is invalid, expected JSON object with profile key"); | ||||||
|  |                                 return Optional.empty(); | ||||||
|  |                             } | ||||||
|  |  | ||||||
|  |                             JsonProfileResult profile = gson.fromJson(pJson.get(), JsonProfileResult.class); | ||||||
|  |                             return value.apply(profile); | ||||||
|  |                         } | ||||||
|  |                     } catch (JsonSyntaxException | InvalidJsonException e) { | ||||||
|  |                         log.error("Unable to parse backend response as JSON", e); | ||||||
|  |                         throw new InternalServerError(e); | ||||||
|  |                     } catch (URISyntaxException e) { | ||||||
|  |                         log.error("Unable to build a valid request URL", e); | ||||||
|  |                         throw new InternalServerError(e); | ||||||
|  |                     } catch (IOException e) { | ||||||
|  |                         log.error("I/O Error during backend request", e); | ||||||
|  |                         throw new InternalServerError(); | ||||||
|  |                     } | ||||||
|  |                 }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public Optional<String> getDisplayName(_MatrixID userId) { | ||||||
|  |         return doRequest(userId, p -> Optional.ofNullable(p.getDisplayName()), profile -> Optional.ofNullable(profile.getDisplayName())); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public List<_ThreePid> getThreepids(_MatrixID userId) { | ||||||
|  |         return doRequest(userId, p -> Optional.ofNullable(p.getThreepids()), profile -> { | ||||||
|  |             List<_ThreePid> t = new ArrayList<>(); | ||||||
|  |             if (Objects.nonNull(profile.getThreepids())) { | ||||||
|  |                 t.addAll(profile.getThreepids()); | ||||||
|  |             } | ||||||
|  |             return Optional.of(t); | ||||||
|  |         }).orElseGet(Collections::emptyList); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public List<String> getRoles(_MatrixID userId) { | ||||||
|  |         return doRequest(userId, p -> Optional.ofNullable(p.getRoles()), profile -> { | ||||||
|  |             List<String> t = new ArrayList<>(); | ||||||
|  |             if (Objects.nonNull(profile.getRoles())) { | ||||||
|  |                 t.addAll(profile.getRoles()); | ||||||
|  |             } | ||||||
|  |             return Optional.of(t); | ||||||
|  |         }).orElseGet(Collections::emptyList); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -0,0 +1,47 @@ | |||||||
|  | /* | ||||||
|  |  * mxisd - Matrix Identity Server Daemon | ||||||
|  |  * 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.mxisd.backend.rest; | ||||||
|  |  | ||||||
|  | import io.kamax.mxisd.Mxisd; | ||||||
|  | import io.kamax.mxisd.auth.AuthProviders; | ||||||
|  | import io.kamax.mxisd.backend.IdentityStoreSupplier; | ||||||
|  | import io.kamax.mxisd.config.MxisdConfig; | ||||||
|  | import io.kamax.mxisd.directory.DirectoryProviders; | ||||||
|  | import io.kamax.mxisd.lookup.ThreePidProviders; | ||||||
|  | import io.kamax.mxisd.profile.ProfileProviders; | ||||||
|  |  | ||||||
|  | public class RestStoreSupplier implements IdentityStoreSupplier { | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void accept(Mxisd mxisd) { | ||||||
|  |         accept(mxisd.getConfig()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void accept(MxisdConfig cfg) { | ||||||
|  |         if (cfg.getRest().isEnabled()) { | ||||||
|  |             AuthProviders.register(() -> new RestAuthProvider(cfg.getRest())); | ||||||
|  |             DirectoryProviders.register(() -> new RestDirectoryProvider(cfg.getRest(), cfg.getMatrix())); | ||||||
|  |             ThreePidProviders.register(() -> new RestThreePidProvider(cfg.getRest(), cfg.getMatrix())); | ||||||
|  |             ProfileProviders.register(() -> new RestProfileProvider(cfg.getRest())); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -35,8 +35,6 @@ import org.apache.http.client.methods.CloseableHttpResponse; | |||||||
| import org.apache.http.client.methods.HttpUriRequest; | import org.apache.http.client.methods.HttpUriRequest; | ||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
| import org.springframework.beans.factory.annotation.Autowired; |  | ||||||
| import org.springframework.stereotype.Component; |  | ||||||
|  |  | ||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
| @@ -44,14 +42,12 @@ import java.util.List; | |||||||
| import java.util.Optional; | import java.util.Optional; | ||||||
| import java.util.stream.Collectors; | import java.util.stream.Collectors; | ||||||
|  |  | ||||||
| @Component |  | ||||||
| public class RestThreePidProvider extends RestProvider implements IThreePidProvider { | public class RestThreePidProvider extends RestProvider implements IThreePidProvider { | ||||||
|  |  | ||||||
|     private Logger log = LoggerFactory.getLogger(RestThreePidProvider.class); |     private transient final Logger log = LoggerFactory.getLogger(RestThreePidProvider.class); | ||||||
|  |  | ||||||
|     private MatrixConfig mxCfg; // FIXME should be done in the lookup manager |     private MatrixConfig mxCfg; // FIXME should be done in the lookup manager | ||||||
|  |  | ||||||
|     @Autowired |  | ||||||
|     public RestThreePidProvider(RestBackendConfig cfg, MatrixConfig mxCfg) { |     public RestThreePidProvider(RestBackendConfig cfg, MatrixConfig mxCfg) { | ||||||
|         super(cfg); |         super(cfg); | ||||||
|         this.mxCfg = mxCfg; |         this.mxCfg = mxCfg; | ||||||
| @@ -66,11 +62,6 @@ public class RestThreePidProvider extends RestProvider implements IThreePidProvi | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public boolean isEnabled() { |  | ||||||
|         return cfg.isEnabled(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public boolean isLocal() { |     public boolean isLocal() { | ||||||
|         return true; |         return true; | ||||||
|   | |||||||
| @@ -0,0 +1,55 @@ | |||||||
|  | /* | ||||||
|  |  * mxisd - Matrix Identity Server Daemon | ||||||
|  |  * Copyright (C) 2019 Kamax Sàrl | ||||||
|  |  * | ||||||
|  |  * https://www.kamax.io/ | ||||||
|  |  * | ||||||
|  |  * This program is free software: you can redistribute it and/or modify | ||||||
|  |  * it under the terms of the GNU Affero General Public License as | ||||||
|  |  * published by the Free Software Foundation, either version 3 of the | ||||||
|  |  * License, or (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  * This program is distributed in the hope that it will be useful, | ||||||
|  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  * GNU Affero General Public License for more details. | ||||||
|  |  * | ||||||
|  |  * You should have received a copy of the GNU Affero General Public License | ||||||
|  |  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package io.kamax.mxisd.backend.sql; | ||||||
|  |  | ||||||
|  | import org.apache.commons.lang3.StringUtils; | ||||||
|  |  | ||||||
|  | public class BuiltInDriverLoader implements DriverLoader { | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void accept(String s) { | ||||||
|  |         String className = null; | ||||||
|  |         if (StringUtils.equals("sqlite", s)) { | ||||||
|  |             className = "org.sqlite.JDBC"; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (StringUtils.equals("postgresql", s)) { | ||||||
|  |             className = "org.postgresql.Driver"; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (StringUtils.equals("mariadb", s)) { | ||||||
|  |             className = "org.mariadb.jdbc.Driver"; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (StringUtils.equals("mysql", s)) { | ||||||
|  |             className = "org.mariadb.jdbc.Driver"; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (StringUtils.isNotEmpty(className)) { | ||||||
|  |             try { | ||||||
|  |                 Class.forName(className); | ||||||
|  |             } catch (ClassNotFoundException e) { | ||||||
|  |                 throw new RuntimeException(e); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
							
								
								
									
										26
									
								
								src/main/java/io/kamax/mxisd/backend/sql/DriverLoader.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								src/main/java/io/kamax/mxisd/backend/sql/DriverLoader.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | |||||||
|  | /* | ||||||
|  |  * mxisd - Matrix Identity Server Daemon | ||||||
|  |  * Copyright (C) 2019 Kamax Sàrl | ||||||
|  |  * | ||||||
|  |  * https://www.kamax.io/ | ||||||
|  |  * | ||||||
|  |  * This program is free software: you can redistribute it and/or modify | ||||||
|  |  * it under the terms of the GNU Affero General Public License as | ||||||
|  |  * published by the Free Software Foundation, either version 3 of the | ||||||
|  |  * License, or (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  * This program is distributed in the hope that it will be useful, | ||||||
|  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  * GNU Affero General Public License for more details. | ||||||
|  |  * | ||||||
|  |  * You should have received a copy of the GNU Affero General Public License | ||||||
|  |  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package io.kamax.mxisd.backend.sql; | ||||||
|  |  | ||||||
|  | import java.util.function.Consumer; | ||||||
|  |  | ||||||
|  | public interface DriverLoader extends Consumer<String> { | ||||||
|  | } | ||||||
| @@ -1,6 +1,6 @@ | |||||||
| /* | /* | ||||||
|  * mxisd - Matrix Identity Server Daemon |  * mxisd - Matrix Identity Server Daemon | ||||||
|  * Copyright (C) 2017 Kamax Sarl |  * Copyright (C) 2019 Kamax Sàrl | ||||||
|  * |  * | ||||||
|  * https://www.kamax.io/ |  * https://www.kamax.io/ | ||||||
|  * |  * | ||||||
| @@ -18,16 +18,21 @@ | |||||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. |  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package io.kamax.mxisd; | package io.kamax.mxisd.backend.sql; | ||||||
| 
 | 
 | ||||||
| import org.springframework.boot.SpringApplication; | import java.util.Objects; | ||||||
| import org.springframework.boot.autoconfigure.SpringBootApplication; | import java.util.ServiceLoader; | ||||||
| 
 | 
 | ||||||
| @SpringBootApplication | public class Drivers { | ||||||
| public class MatrixIdentityServerApplication { |  | ||||||
| 
 | 
 | ||||||
|     public static void main(String[] args) { |     private static ServiceLoader<DriverLoader> svcLoader; | ||||||
|         SpringApplication.run(MatrixIdentityServerApplication.class, args); | 
 | ||||||
|  |     public static void load(String type) { | ||||||
|  |         if (Objects.isNull(svcLoader)) { | ||||||
|  |             svcLoader = ServiceLoader.load(DriverLoader.class); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         svcLoader.iterator().forEachRemaining(drv -> drv.accept(type)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
| @@ -37,11 +37,15 @@ public class SqlConnectionPool { | |||||||
|     private ComboPooledDataSource ds; |     private ComboPooledDataSource ds; | ||||||
|  |  | ||||||
|     public SqlConnectionPool(SqlConfig cfg) { |     public SqlConnectionPool(SqlConfig cfg) { | ||||||
|  |         Drivers.load(cfg.getType()); | ||||||
|  |  | ||||||
|         ds = new ComboPooledDataSource(); |         ds = new ComboPooledDataSource(); | ||||||
|         ds.setJdbcUrl("jdbc:" + cfg.getType() + ":" + cfg.getConnection()); |         ds.setJdbcUrl("jdbc:" + cfg.getType() + ":" + cfg.getConnection()); | ||||||
|         ds.setMinPoolSize(1); |         ds.setMinPoolSize(1); | ||||||
|         ds.setMaxPoolSize(10); |         ds.setMaxPoolSize(10); | ||||||
|         ds.setAcquireIncrement(2); |         ds.setAcquireIncrement(2); | ||||||
|  |         ds.setAcquireRetryAttempts(10); | ||||||
|  |         ds.setAcquireRetryDelay(1000); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public Connection get() throws SQLException { |     public Connection get() throws SQLException { | ||||||
|   | |||||||
| @@ -39,7 +39,7 @@ import java.util.Optional; | |||||||
|  |  | ||||||
| public abstract class SqlProfileProvider implements ProfileProvider { | public abstract class SqlProfileProvider implements ProfileProvider { | ||||||
|  |  | ||||||
|     private Logger log = LoggerFactory.getLogger(SqlProfileProvider.class); |     private transient final Logger log = LoggerFactory.getLogger(SqlProfileProvider.class); | ||||||
|  |  | ||||||
|     private SqlConfig.Profile cfg; |     private SqlConfig.Profile cfg; | ||||||
|  |  | ||||||
| @@ -50,11 +50,6 @@ public abstract class SqlProfileProvider implements ProfileProvider { | |||||||
|         this.pool = new SqlConnectionPool(cfg); |         this.pool = new SqlConnectionPool(cfg); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public boolean isEnabled() { |  | ||||||
|         return cfg.isEnabled(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public Optional<String> getDisplayName(_MatrixID user) { |     public Optional<String> getDisplayName(_MatrixID user) { | ||||||
|         String stmtSql = cfg.getDisplayName().getQuery(); |         String stmtSql = cfg.getDisplayName().getQuery(); | ||||||
|   | |||||||
| @@ -41,7 +41,7 @@ import java.util.Optional; | |||||||
|  |  | ||||||
| public abstract class SqlThreePidProvider implements IThreePidProvider { | public abstract class SqlThreePidProvider implements IThreePidProvider { | ||||||
|  |  | ||||||
|     private Logger log = LoggerFactory.getLogger(SqlThreePidProvider.class); |     private transient final Logger log = LoggerFactory.getLogger(SqlThreePidProvider.class); | ||||||
|  |  | ||||||
|     private SqlConfig cfg; |     private SqlConfig cfg; | ||||||
|     private MatrixConfig mxCfg; |     private MatrixConfig mxCfg; | ||||||
| @@ -54,11 +54,6 @@ public abstract class SqlThreePidProvider implements IThreePidProvider { | |||||||
|         this.mxCfg = mxCfg; |         this.mxCfg = mxCfg; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public boolean isEnabled() { |  | ||||||
|         return cfg.isEnabled(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public boolean isLocal() { |     public boolean isLocal() { | ||||||
|         return true; |         return true; | ||||||
|   | |||||||
| @@ -27,20 +27,19 @@ import io.kamax.mxisd.config.sql.generic.GenericSqlProviderConfig; | |||||||
| import io.kamax.mxisd.invitation.InvitationManager; | import io.kamax.mxisd.invitation.InvitationManager; | ||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
| import org.springframework.beans.factory.annotation.Autowired; |  | ||||||
| import org.springframework.stereotype.Component; |  | ||||||
|  |  | ||||||
| @Component |  | ||||||
| public class GenericSqlAuthProvider implements AuthenticatorProvider { | public class GenericSqlAuthProvider implements AuthenticatorProvider { | ||||||
|  |  | ||||||
|     private Logger log = LoggerFactory.getLogger(GenericSqlAuthProvider.class); |     private transient final Logger log = LoggerFactory.getLogger(GenericSqlAuthProvider.class); | ||||||
|  |  | ||||||
|     @Autowired |  | ||||||
|     private GenericSqlProviderConfig cfg; |     private GenericSqlProviderConfig cfg; | ||||||
|  |  | ||||||
|     @Autowired |  | ||||||
|     private InvitationManager invMgr; |     private InvitationManager invMgr; | ||||||
|  |  | ||||||
|  |     public GenericSqlAuthProvider(GenericSqlProviderConfig cfg, InvitationManager invMgr) { | ||||||
|  |         this.cfg = cfg; | ||||||
|  |         this.invMgr = invMgr; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public boolean isEnabled() { |     public boolean isEnabled() { | ||||||
|         return cfg.getAuth().isEnabled(); |         return cfg.getAuth().isEnabled(); | ||||||
|   | |||||||
| @@ -25,9 +25,9 @@ import io.kamax.mxisd.backend.sql.SqlConnectionPool; | |||||||
| import io.kamax.mxisd.config.MatrixConfig; | import io.kamax.mxisd.config.MatrixConfig; | ||||||
| import io.kamax.mxisd.config.sql.SqlConfig; | import io.kamax.mxisd.config.sql.SqlConfig; | ||||||
| import io.kamax.mxisd.config.sql.generic.GenericSqlProviderConfig; | import io.kamax.mxisd.config.sql.generic.GenericSqlProviderConfig; | ||||||
| import io.kamax.mxisd.controller.directory.v1.io.UserDirectorySearchResult; | import io.kamax.mxisd.directory.DirectoryProvider; | ||||||
| import io.kamax.mxisd.directory.IDirectoryProvider; |  | ||||||
| import io.kamax.mxisd.exception.InternalServerError; | import io.kamax.mxisd.exception.InternalServerError; | ||||||
|  | import io.kamax.mxisd.http.io.UserDirectorySearchResult; | ||||||
| import org.apache.commons.lang.StringUtils; | import org.apache.commons.lang.StringUtils; | ||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
| @@ -38,11 +38,11 @@ import java.sql.ResultSet; | |||||||
| import java.sql.SQLException; | import java.sql.SQLException; | ||||||
| import java.util.Optional; | import java.util.Optional; | ||||||
|  |  | ||||||
| import static io.kamax.mxisd.controller.directory.v1.io.UserDirectorySearchResult.Result; | import static io.kamax.mxisd.http.io.UserDirectorySearchResult.Result; | ||||||
|  |  | ||||||
| public abstract class GenericSqlDirectoryProvider implements IDirectoryProvider { | public class GenericSqlDirectoryProvider implements DirectoryProvider { | ||||||
|  |  | ||||||
|     private Logger log = LoggerFactory.getLogger(GenericSqlDirectoryProvider.class); |     private transient final Logger log = LoggerFactory.getLogger(GenericSqlDirectoryProvider.class); | ||||||
|  |  | ||||||
|     protected SqlConfig cfg; |     protected SqlConfig cfg; | ||||||
|     protected MatrixConfig mxCfg; |     protected MatrixConfig mxCfg; | ||||||
| @@ -55,11 +55,6 @@ public abstract class GenericSqlDirectoryProvider implements IDirectoryProvider | |||||||
|         this.mxCfg = mxCfg; |         this.mxCfg = mxCfg; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public boolean isEnabled() { |  | ||||||
|         return cfg.getDirectory().isEnabled(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     protected void setParameters(PreparedStatement stmt, String searchTerm) throws SQLException { |     protected void setParameters(PreparedStatement stmt, String searchTerm) throws SQLException { | ||||||
|         for (int i = 1; i <= stmt.getParameterMetaData().getParameterCount(); i++) { |         for (int i = 1; i <= stmt.getParameterMetaData().getParameterCount(); i++) { | ||||||
|             stmt.setString(i, searchTerm); |             stmt.setString(i, searchTerm); | ||||||
|   | |||||||
| @@ -22,9 +22,7 @@ package io.kamax.mxisd.backend.sql.generic; | |||||||
|  |  | ||||||
| import io.kamax.mxisd.backend.sql.SqlProfileProvider; | import io.kamax.mxisd.backend.sql.SqlProfileProvider; | ||||||
| import io.kamax.mxisd.config.sql.generic.GenericSqlProviderConfig; | import io.kamax.mxisd.config.sql.generic.GenericSqlProviderConfig; | ||||||
| import org.springframework.stereotype.Component; |  | ||||||
|  |  | ||||||
| @Component |  | ||||||
| public class GenericSqlProfileProvider extends SqlProfileProvider { | public class GenericSqlProfileProvider extends SqlProfileProvider { | ||||||
|  |  | ||||||
|     public GenericSqlProfileProvider(GenericSqlProviderConfig cfg) { |     public GenericSqlProfileProvider(GenericSqlProviderConfig cfg) { | ||||||
|   | |||||||
| @@ -0,0 +1,51 @@ | |||||||
|  | /* | ||||||
|  |  * mxisd - Matrix Identity Server Daemon | ||||||
|  |  * 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.mxisd.backend.sql.generic; | ||||||
|  |  | ||||||
|  | import io.kamax.mxisd.Mxisd; | ||||||
|  | import io.kamax.mxisd.auth.AuthProviders; | ||||||
|  | import io.kamax.mxisd.backend.IdentityStoreSupplier; | ||||||
|  | import io.kamax.mxisd.directory.DirectoryProviders; | ||||||
|  | import io.kamax.mxisd.lookup.ThreePidProviders; | ||||||
|  | import io.kamax.mxisd.profile.ProfileProviders; | ||||||
|  |  | ||||||
|  | public class GenericSqlStoreSupplier implements IdentityStoreSupplier { | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void accept(Mxisd mxisd) { | ||||||
|  |         if (mxisd.getConfig().getSql().getAuth().isEnabled()) { | ||||||
|  |             AuthProviders.register(() -> new GenericSqlAuthProvider(mxisd.getConfig().getSql(), mxisd.getInvitationManager())); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (mxisd.getConfig().getSql().getDirectory().isEnabled()) { | ||||||
|  |             DirectoryProviders.register(() -> new GenericSqlDirectoryProvider(mxisd.getConfig().getSql(), mxisd.getConfig().getMatrix())); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (mxisd.getConfig().getSql().getIdentity().isEnabled()) { | ||||||
|  |             ThreePidProviders.register(() -> new GenericSqlThreePidProvider(mxisd.getConfig().getSql(), mxisd.getConfig().getMatrix())); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (mxisd.getConfig().getSql().getProfile().isEnabled()) { | ||||||
|  |             ProfileProviders.register(() -> new GenericSqlProfileProvider(mxisd.getConfig().getSql())); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -23,13 +23,9 @@ package io.kamax.mxisd.backend.sql.generic; | |||||||
| import io.kamax.mxisd.backend.sql.SqlThreePidProvider; | import io.kamax.mxisd.backend.sql.SqlThreePidProvider; | ||||||
| import io.kamax.mxisd.config.MatrixConfig; | import io.kamax.mxisd.config.MatrixConfig; | ||||||
| import io.kamax.mxisd.config.sql.generic.GenericSqlProviderConfig; | import io.kamax.mxisd.config.sql.generic.GenericSqlProviderConfig; | ||||||
| import org.springframework.beans.factory.annotation.Autowired; |  | ||||||
| import org.springframework.stereotype.Component; |  | ||||||
|  |  | ||||||
| @Component |  | ||||||
| public class GenericSqlThreePidProvider extends SqlThreePidProvider { | public class GenericSqlThreePidProvider extends SqlThreePidProvider { | ||||||
|  |  | ||||||
|     @Autowired |  | ||||||
|     public GenericSqlThreePidProvider(GenericSqlProviderConfig cfg, MatrixConfig mxCfg) { |     public GenericSqlThreePidProvider(GenericSqlProviderConfig cfg, MatrixConfig mxCfg) { | ||||||
|         super(cfg, mxCfg); |         super(cfg, mxCfg); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -22,19 +22,15 @@ package io.kamax.mxisd.backend.sql.synapse; | |||||||
|  |  | ||||||
| import io.kamax.mxisd.backend.sql.SqlConnectionPool; | import io.kamax.mxisd.backend.sql.SqlConnectionPool; | ||||||
| import io.kamax.mxisd.config.sql.synapse.SynapseSqlProviderConfig; | import io.kamax.mxisd.config.sql.synapse.SynapseSqlProviderConfig; | ||||||
| import org.springframework.beans.factory.annotation.Autowired; |  | ||||||
| import org.springframework.stereotype.Component; |  | ||||||
|  |  | ||||||
| import java.sql.PreparedStatement; | import java.sql.PreparedStatement; | ||||||
| import java.sql.ResultSet; | import java.sql.ResultSet; | ||||||
| import java.util.Optional; | import java.util.Optional; | ||||||
|  |  | ||||||
| @Component |  | ||||||
| public class Synapse { | public class Synapse { | ||||||
|  |  | ||||||
|     private SqlConnectionPool pool; |     private SqlConnectionPool pool; | ||||||
|  |  | ||||||
|     @Autowired |  | ||||||
|     public Synapse(SynapseSqlProviderConfig sqlCfg) { |     public Synapse(SynapseSqlProviderConfig sqlCfg) { | ||||||
|         this.pool = new SqlConnectionPool(sqlCfg); |         this.pool = new SqlConnectionPool(sqlCfg); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -24,32 +24,15 @@ import io.kamax.mxisd.backend.sql.generic.GenericSqlDirectoryProvider; | |||||||
| import io.kamax.mxisd.config.MatrixConfig; | import io.kamax.mxisd.config.MatrixConfig; | ||||||
| import io.kamax.mxisd.config.sql.generic.GenericSqlProviderConfig; | import io.kamax.mxisd.config.sql.generic.GenericSqlProviderConfig; | ||||||
| import io.kamax.mxisd.config.sql.synapse.SynapseSqlProviderConfig; | import io.kamax.mxisd.config.sql.synapse.SynapseSqlProviderConfig; | ||||||
| import org.springframework.beans.factory.annotation.Autowired; |  | ||||||
| import org.springframework.stereotype.Component; |  | ||||||
|  |  | ||||||
| import javax.annotation.PostConstruct; |  | ||||||
| import java.sql.PreparedStatement; | import java.sql.PreparedStatement; | ||||||
| import java.sql.SQLException; | import java.sql.SQLException; | ||||||
| import java.util.Objects; | import java.util.Objects; | ||||||
|  |  | ||||||
| @Component |  | ||||||
| public class SynapseSqlDirectoryProvider extends GenericSqlDirectoryProvider { | public class SynapseSqlDirectoryProvider extends GenericSqlDirectoryProvider { | ||||||
|  |  | ||||||
|     @Autowired |  | ||||||
|     public SynapseSqlDirectoryProvider(SynapseSqlProviderConfig cfg, MatrixConfig mxCfg) { |     public SynapseSqlDirectoryProvider(SynapseSqlProviderConfig cfg, MatrixConfig mxCfg) { | ||||||
|         super(cfg, mxCfg); |         super(cfg, mxCfg); | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     protected void setParameters(PreparedStatement stmt, String searchTerm) throws SQLException { |  | ||||||
|         stmt.setString(1, "%" + searchTerm + "%"); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @PostConstruct |  | ||||||
|     public void build() { |  | ||||||
|         if (!isEnabled()) { |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         GenericSqlProviderConfig.Type queries = cfg.getDirectory().getQuery(); |         GenericSqlProviderConfig.Type queries = cfg.getDirectory().getQuery(); | ||||||
|         if (Objects.isNull(queries.getName().getValue())) { |         if (Objects.isNull(queries.getName().getValue())) { | ||||||
| @@ -60,4 +43,9 @@ public class SynapseSqlDirectoryProvider extends GenericSqlDirectoryProvider { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     protected void setParameters(PreparedStatement stmt, String searchTerm) throws SQLException { | ||||||
|  |         stmt.setString(1, "%" + searchTerm + "%"); | ||||||
|  |     } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -22,13 +22,9 @@ package io.kamax.mxisd.backend.sql.synapse; | |||||||
|  |  | ||||||
| import io.kamax.mxisd.backend.sql.SqlProfileProvider; | import io.kamax.mxisd.backend.sql.SqlProfileProvider; | ||||||
| import io.kamax.mxisd.config.sql.synapse.SynapseSqlProviderConfig; | import io.kamax.mxisd.config.sql.synapse.SynapseSqlProviderConfig; | ||||||
| import org.springframework.beans.factory.annotation.Autowired; |  | ||||||
| import org.springframework.stereotype.Component; |  | ||||||
|  |  | ||||||
| @Component |  | ||||||
| public class SynapseSqlProfileProvider extends SqlProfileProvider { | public class SynapseSqlProfileProvider extends SqlProfileProvider { | ||||||
|  |  | ||||||
|     @Autowired |  | ||||||
|     public SynapseSqlProfileProvider(SynapseSqlProviderConfig cfg) { |     public SynapseSqlProfileProvider(SynapseSqlProviderConfig cfg) { | ||||||
|         super(cfg); |         super(cfg); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -0,0 +1,51 @@ | |||||||
|  | /* | ||||||
|  |  * mxisd - Matrix Identity Server Daemon | ||||||
|  |  * 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.mxisd.backend.sql.synapse; | ||||||
|  |  | ||||||
|  | import io.kamax.mxisd.Mxisd; | ||||||
|  | import io.kamax.mxisd.backend.IdentityStoreSupplier; | ||||||
|  | import io.kamax.mxisd.config.MxisdConfig; | ||||||
|  | import io.kamax.mxisd.directory.DirectoryProviders; | ||||||
|  | import io.kamax.mxisd.lookup.ThreePidProviders; | ||||||
|  | import io.kamax.mxisd.profile.ProfileProviders; | ||||||
|  |  | ||||||
|  | public class SynapseSqlStoreSupplier implements IdentityStoreSupplier { | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void accept(Mxisd mxisd) { | ||||||
|  |         accept(mxisd.getConfig()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void accept(MxisdConfig cfg) { | ||||||
|  |         if (cfg.getSynapseSql().getDirectory().isEnabled()) { | ||||||
|  |             DirectoryProviders.register(() -> new SynapseSqlDirectoryProvider(cfg.getSynapseSql(), cfg.getMatrix())); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (cfg.getSynapseSql().getIdentity().isEnabled()) { | ||||||
|  |             ThreePidProviders.register(() -> new SynapseSqlThreePidProvider(cfg.getSynapseSql(), cfg.getMatrix())); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (cfg.getSynapseSql().getProfile().isEnabled()) { | ||||||
|  |             ProfileProviders.register(() -> new SynapseSqlProfileProvider(cfg.getSynapseSql())); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -23,13 +23,9 @@ package io.kamax.mxisd.backend.sql.synapse; | |||||||
| import io.kamax.mxisd.backend.sql.SqlThreePidProvider; | import io.kamax.mxisd.backend.sql.SqlThreePidProvider; | ||||||
| import io.kamax.mxisd.config.MatrixConfig; | import io.kamax.mxisd.config.MatrixConfig; | ||||||
| import io.kamax.mxisd.config.sql.synapse.SynapseSqlProviderConfig; | import io.kamax.mxisd.config.sql.synapse.SynapseSqlProviderConfig; | ||||||
| import org.springframework.beans.factory.annotation.Autowired; |  | ||||||
| import org.springframework.stereotype.Component; |  | ||||||
|  |  | ||||||
| @Component |  | ||||||
| public class SynapseSqlThreePidProvider extends SqlThreePidProvider { | public class SynapseSqlThreePidProvider extends SqlThreePidProvider { | ||||||
|  |  | ||||||
|     @Autowired |  | ||||||
|     public SynapseSqlThreePidProvider(SynapseSqlProviderConfig cfg, MatrixConfig mxCfg) { |     public SynapseSqlThreePidProvider(SynapseSqlProviderConfig cfg, MatrixConfig mxCfg) { | ||||||
|         super(cfg, mxCfg); |         super(cfg, mxCfg); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -28,17 +28,13 @@ import io.kamax.mxisd.auth.provider.BackendAuthResult; | |||||||
| import org.apache.commons.lang.StringUtils; | import org.apache.commons.lang.StringUtils; | ||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
| import org.springframework.beans.factory.annotation.Autowired; |  | ||||||
| import org.springframework.stereotype.Component; |  | ||||||
|  |  | ||||||
| @Component |  | ||||||
| public class WordpressAuthProvider implements AuthenticatorProvider { | public class WordpressAuthProvider implements AuthenticatorProvider { | ||||||
|  |  | ||||||
|     private final Logger log = LoggerFactory.getLogger(WordpressAuthProvider.class); |     private transient final Logger log = LoggerFactory.getLogger(WordpressAuthProvider.class); | ||||||
|  |  | ||||||
|     private WordpressRestBackend wordpress; |     private WordpressRestBackend wordpress; | ||||||
|  |  | ||||||
|     @Autowired |  | ||||||
|     public WordpressAuthProvider(WordpressRestBackend wordpress) { |     public WordpressAuthProvider(WordpressRestBackend wordpress) { | ||||||
|         this.wordpress = wordpress; |         this.wordpress = wordpress; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -23,13 +23,11 @@ package io.kamax.mxisd.backend.wordpress; | |||||||
| import io.kamax.matrix.MatrixID; | import io.kamax.matrix.MatrixID; | ||||||
| import io.kamax.mxisd.config.MatrixConfig; | import io.kamax.mxisd.config.MatrixConfig; | ||||||
| import io.kamax.mxisd.config.wordpress.WordpressConfig; | import io.kamax.mxisd.config.wordpress.WordpressConfig; | ||||||
| import io.kamax.mxisd.controller.directory.v1.io.UserDirectorySearchResult; | import io.kamax.mxisd.directory.DirectoryProvider; | ||||||
| import io.kamax.mxisd.directory.IDirectoryProvider; |  | ||||||
| import io.kamax.mxisd.exception.InternalServerError; | import io.kamax.mxisd.exception.InternalServerError; | ||||||
|  | import io.kamax.mxisd.http.io.UserDirectorySearchResult; | ||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
| import org.springframework.beans.factory.annotation.Autowired; |  | ||||||
| import org.springframework.stereotype.Component; |  | ||||||
|  |  | ||||||
| import java.sql.Connection; | import java.sql.Connection; | ||||||
| import java.sql.PreparedStatement; | import java.sql.PreparedStatement; | ||||||
| @@ -37,27 +35,20 @@ import java.sql.ResultSet; | |||||||
| import java.sql.SQLException; | import java.sql.SQLException; | ||||||
| import java.util.Optional; | import java.util.Optional; | ||||||
|  |  | ||||||
| @Component | public class WordpressDirectoryProvider implements DirectoryProvider { | ||||||
| public class WordpressDirectoryProvider implements IDirectoryProvider { |  | ||||||
|  |  | ||||||
|     private final Logger log = LoggerFactory.getLogger(WordpressDirectoryProvider.class); |     private transient final Logger log = LoggerFactory.getLogger(WordpressDirectoryProvider.class); | ||||||
|  |  | ||||||
|     private WordpressConfig cfg; |     private WordpressConfig cfg; | ||||||
|     private WordressSqlBackend wordpress; |     private WordressSqlBackend wordpress; | ||||||
|     private MatrixConfig mxCfg; |     private MatrixConfig mxCfg; | ||||||
|  |  | ||||||
|     @Autowired |  | ||||||
|     public WordpressDirectoryProvider(WordpressConfig cfg, WordressSqlBackend wordpress, MatrixConfig mxCfg) { |     public WordpressDirectoryProvider(WordpressConfig cfg, WordressSqlBackend wordpress, MatrixConfig mxCfg) { | ||||||
|         this.cfg = cfg; |         this.cfg = cfg; | ||||||
|         this.wordpress = wordpress; |         this.wordpress = wordpress; | ||||||
|         this.mxCfg = mxCfg; |         this.mxCfg = mxCfg; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public boolean isEnabled() { |  | ||||||
|         return wordpress.isEnabled(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     protected void setParameters(PreparedStatement stmt, String searchTerm) throws SQLException { |     protected void setParameters(PreparedStatement stmt, String searchTerm) throws SQLException { | ||||||
|         for (int i = 1; i <= stmt.getParameterMetaData().getParameterCount(); i++) { |         for (int i = 1; i <= stmt.getParameterMetaData().getParameterCount(); i++) { | ||||||
|             stmt.setString(i, "%" + searchTerm + "%"); |             stmt.setString(i, "%" + searchTerm + "%"); | ||||||
|   | |||||||
| @@ -34,15 +34,12 @@ import org.apache.http.impl.client.CloseableHttpClient; | |||||||
| import org.apache.http.util.EntityUtils; | import org.apache.http.util.EntityUtils; | ||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
| import org.springframework.beans.factory.annotation.Autowired; |  | ||||||
| import org.springframework.stereotype.Component; |  | ||||||
|  |  | ||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
|  |  | ||||||
| @Component |  | ||||||
| public class WordpressRestBackend { | public class WordpressRestBackend { | ||||||
|  |  | ||||||
|     private final Logger log = LoggerFactory.getLogger(WordpressRestBackend.class); |     private transient final Logger log = LoggerFactory.getLogger(WordpressRestBackend.class); | ||||||
|     private final String jsonPath = "/wp-json"; |     private final String jsonPath = "/wp-json"; | ||||||
|     private final String jwtPath = "/jwt-auth/v1"; |     private final String jwtPath = "/jwt-auth/v1"; | ||||||
|  |  | ||||||
| @@ -54,7 +51,6 @@ public class WordpressRestBackend { | |||||||
|  |  | ||||||
|     private String token; |     private String token; | ||||||
|  |  | ||||||
|     @Autowired |  | ||||||
|     public WordpressRestBackend(WordpressConfig cfg, CloseableHttpClient client) { |     public WordpressRestBackend(WordpressConfig cfg, CloseableHttpClient client) { | ||||||
|         this.cfg = cfg; |         this.cfg = cfg; | ||||||
|         this.client = client; |         this.client = client; | ||||||
|   | |||||||
| @@ -0,0 +1,48 @@ | |||||||
|  | /* | ||||||
|  |  * mxisd - Matrix Identity Server Daemon | ||||||
|  |  * 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.mxisd.backend.wordpress; | ||||||
|  |  | ||||||
|  | import io.kamax.mxisd.Mxisd; | ||||||
|  | import io.kamax.mxisd.auth.AuthProviders; | ||||||
|  | import io.kamax.mxisd.backend.IdentityStoreSupplier; | ||||||
|  | import io.kamax.mxisd.config.MatrixConfig; | ||||||
|  | import io.kamax.mxisd.config.wordpress.WordpressConfig; | ||||||
|  | import io.kamax.mxisd.directory.DirectoryProviders; | ||||||
|  | import io.kamax.mxisd.lookup.ThreePidProviders; | ||||||
|  |  | ||||||
|  | public class WordpressStoreSupplier implements IdentityStoreSupplier { | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void accept(Mxisd m) { | ||||||
|  |         WordpressConfig wpCfg = m.getConfig().getWordpress(); | ||||||
|  |         MatrixConfig mxCfg = m.getConfig().getMatrix(); | ||||||
|  |  | ||||||
|  |         if (m.getConfig().getWordpress().isEnabled()) { | ||||||
|  |             WordpressRestBackend restBackend = new WordpressRestBackend(wpCfg, m.getHttpClient()); | ||||||
|  |             WordressSqlBackend sqlBackend = new WordressSqlBackend(wpCfg); | ||||||
|  |  | ||||||
|  |             AuthProviders.register(() -> new WordpressAuthProvider(restBackend)); | ||||||
|  |             DirectoryProviders.register(() -> new WordpressDirectoryProvider(wpCfg, sqlBackend, mxCfg)); | ||||||
|  |             ThreePidProviders.register(() -> new WordpressThreePidProvider(mxCfg, wpCfg, sqlBackend)); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -31,8 +31,6 @@ import io.kamax.mxisd.lookup.ThreePidMapping; | |||||||
| import io.kamax.mxisd.lookup.provider.IThreePidProvider; | import io.kamax.mxisd.lookup.provider.IThreePidProvider; | ||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
| import org.springframework.beans.factory.annotation.Autowired; |  | ||||||
| import org.springframework.stereotype.Component; |  | ||||||
|  |  | ||||||
| import java.sql.Connection; | import java.sql.Connection; | ||||||
| import java.sql.PreparedStatement; | import java.sql.PreparedStatement; | ||||||
| @@ -42,27 +40,20 @@ import java.util.List; | |||||||
| import java.util.Objects; | import java.util.Objects; | ||||||
| import java.util.Optional; | import java.util.Optional; | ||||||
|  |  | ||||||
| @Component |  | ||||||
| public class WordpressThreePidProvider implements IThreePidProvider { | public class WordpressThreePidProvider implements IThreePidProvider { | ||||||
|  |  | ||||||
|     private final Logger log = LoggerFactory.getLogger(WordpressThreePidProvider.class); |     private transient final Logger log = LoggerFactory.getLogger(WordpressThreePidProvider.class); | ||||||
|  |  | ||||||
|     private MatrixConfig mxCfg; |     private MatrixConfig mxCfg; | ||||||
|     private WordpressConfig cfg; |     private WordpressConfig cfg; | ||||||
|     private WordressSqlBackend wordpress; |     private WordressSqlBackend wordpress; | ||||||
|  |  | ||||||
|     @Autowired |  | ||||||
|     public WordpressThreePidProvider(MatrixConfig mxCfg, WordpressConfig cfg, WordressSqlBackend wordpress) { |     public WordpressThreePidProvider(MatrixConfig mxCfg, WordpressConfig cfg, WordressSqlBackend wordpress) { | ||||||
|         this.mxCfg = mxCfg; |         this.mxCfg = mxCfg; | ||||||
|         this.cfg = cfg; |         this.cfg = cfg; | ||||||
|         this.wordpress = wordpress; |         this.wordpress = wordpress; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public boolean isEnabled() { |  | ||||||
|         return wordpress.isEnabled(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public boolean isLocal() { |     public boolean isLocal() { | ||||||
|         return true; |         return true; | ||||||
|   | |||||||
| @@ -24,22 +24,17 @@ import com.mchange.v2.c3p0.ComboPooledDataSource; | |||||||
| import io.kamax.mxisd.config.wordpress.WordpressConfig; | import io.kamax.mxisd.config.wordpress.WordpressConfig; | ||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
| import org.springframework.beans.factory.annotation.Autowired; |  | ||||||
| import org.springframework.stereotype.Component; |  | ||||||
|  |  | ||||||
| import java.sql.Connection; | import java.sql.Connection; | ||||||
| import java.sql.SQLException; | import java.sql.SQLException; | ||||||
|  |  | ||||||
| @Component |  | ||||||
| public class WordressSqlBackend { | public class WordressSqlBackend { | ||||||
|  |  | ||||||
|     private Logger log = LoggerFactory.getLogger(WordressSqlBackend.class); |     private transient final Logger log = LoggerFactory.getLogger(WordressSqlBackend.class); | ||||||
|  |  | ||||||
|     private WordpressConfig cfg; |     private WordpressConfig cfg; | ||||||
|  |  | ||||||
|     private ComboPooledDataSource ds; |     private ComboPooledDataSource ds; | ||||||
|  |  | ||||||
|     @Autowired |  | ||||||
|     public WordressSqlBackend(WordpressConfig cfg) { |     public WordressSqlBackend(WordpressConfig cfg) { | ||||||
|         this.cfg = cfg; |         this.cfg = cfg; | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										103
									
								
								src/main/java/io/kamax/mxisd/config/AuthenticationConfig.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								src/main/java/io/kamax/mxisd/config/AuthenticationConfig.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,103 @@ | |||||||
|  | /* | ||||||
|  |  * mxisd - Matrix Identity Server Daemon | ||||||
|  |  * 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.mxisd.config; | ||||||
|  |  | ||||||
|  | import java.util.ArrayList; | ||||||
|  | import java.util.List; | ||||||
|  | import java.util.regex.Pattern; | ||||||
|  |  | ||||||
|  | public class AuthenticationConfig { | ||||||
|  |  | ||||||
|  |     public static class Rule { | ||||||
|  |  | ||||||
|  |         private String regex; | ||||||
|  |         private transient Pattern pattern; | ||||||
|  |         private String medium; | ||||||
|  |  | ||||||
|  |         public String getRegex() { | ||||||
|  |             return regex; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public void setRegex(String regex) { | ||||||
|  |             this.regex = regex; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public Pattern getPattern() { | ||||||
|  |             return pattern; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public void setPattern(Pattern pattern) { | ||||||
|  |             this.pattern = pattern; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public String getMedium() { | ||||||
|  |             return medium; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public void setMedium(String medium) { | ||||||
|  |             this.medium = medium; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static class User { | ||||||
|  |  | ||||||
|  |         private List<Rule> rules = new ArrayList<>(); | ||||||
|  |  | ||||||
|  |         public List<Rule> getRules() { | ||||||
|  |             return rules; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public void setRules(List<Rule> mappings) { | ||||||
|  |             this.rules = mappings; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static class Rewrite { | ||||||
|  |  | ||||||
|  |         private User user = new User(); | ||||||
|  |  | ||||||
|  |         public User getUser() { | ||||||
|  |             return user; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public void setUser(User user) { | ||||||
|  |             this.user = user; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private Rewrite rewrite = new Rewrite(); | ||||||
|  |  | ||||||
|  |     public Rewrite getRewrite() { | ||||||
|  |         return rewrite; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void setRewrite(Rewrite rewrite) { | ||||||
|  |         this.rewrite = rewrite; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void build() { | ||||||
|  |         getRewrite().getUser().getRules().forEach(mapping -> mapping.setPattern(Pattern.compile(mapping.getRegex()))); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -20,14 +20,8 @@ | |||||||
|  |  | ||||||
| package io.kamax.mxisd.config; | package io.kamax.mxisd.config; | ||||||
|  |  | ||||||
| import org.springframework.boot.context.properties.ConfigurationProperties; |  | ||||||
| import org.springframework.context.annotation.Configuration; |  | ||||||
|  |  | ||||||
| import javax.annotation.PostConstruct; |  | ||||||
| import java.util.Objects; | import java.util.Objects; | ||||||
|  |  | ||||||
| @Configuration |  | ||||||
| @ConfigurationProperties(prefix = "lookup.bulk") |  | ||||||
| public class BulkLookupConfig { | public class BulkLookupConfig { | ||||||
|  |  | ||||||
|     private Boolean enabled; |     private Boolean enabled; | ||||||
| @@ -40,7 +34,6 @@ public class BulkLookupConfig { | |||||||
|         this.enabled = enabled; |         this.enabled = enabled; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @PostConstruct |  | ||||||
|     public void build() { |     public void build() { | ||||||
|         if (Objects.isNull(enabled)) { |         if (Objects.isNull(enabled)) { | ||||||
|             enabled = true; |             enabled = true; | ||||||
|   | |||||||
| @@ -22,16 +22,10 @@ package io.kamax.mxisd.config; | |||||||
|  |  | ||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
| import org.springframework.boot.context.properties.ConfigurationProperties; |  | ||||||
| import org.springframework.context.annotation.Configuration; |  | ||||||
|  |  | ||||||
| import javax.annotation.PostConstruct; |  | ||||||
|  |  | ||||||
| @Configuration |  | ||||||
| @ConfigurationProperties("directory") |  | ||||||
| public class DirectoryConfig { | public class DirectoryConfig { | ||||||
|  |  | ||||||
|     private final transient Logger log = LoggerFactory.getLogger(DnsOverwriteConfig.class); |     private final static Logger log = LoggerFactory.getLogger(DirectoryConfig.class); | ||||||
|  |  | ||||||
|     public static class Exclude { |     public static class Exclude { | ||||||
|  |  | ||||||
| @@ -67,12 +61,11 @@ public class DirectoryConfig { | |||||||
|         this.exclude = exclude; |         this.exclude = exclude; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @PostConstruct |     public void build() { | ||||||
|     public void buid() { |  | ||||||
|         log.info("--- Directory config ---"); |         log.info("--- Directory config ---"); | ||||||
|         log.info("Exclude:"); |         log.info("Exclude:"); | ||||||
|         log.info("\tHomeserver: {}", getExclude().getHomeserver()); |         log.info("  Homeserver: {}", getExclude().getHomeserver()); | ||||||
|         log.info("\t3PID: {}", getExclude().getThreepid()); |         log.info("  3PID: {}", getExclude().getThreepid()); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -24,18 +24,13 @@ import com.google.gson.Gson; | |||||||
| import io.kamax.mxisd.util.GsonUtil; | import io.kamax.mxisd.util.GsonUtil; | ||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
| import org.springframework.boot.context.properties.ConfigurationProperties; |  | ||||||
| import org.springframework.context.annotation.Configuration; |  | ||||||
|  |  | ||||||
| import javax.annotation.PostConstruct; |  | ||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
| import java.util.List; | import java.util.List; | ||||||
|  |  | ||||||
| @Configuration |  | ||||||
| @ConfigurationProperties("dns.overwrite") |  | ||||||
| public class DnsOverwriteConfig { | public class DnsOverwriteConfig { | ||||||
|  |  | ||||||
|     private Logger log = LoggerFactory.getLogger(DnsOverwriteConfig.class); |     private transient final Logger log = LoggerFactory.getLogger(DnsOverwriteConfig.class); | ||||||
|  |  | ||||||
|     public static class Entry { |     public static class Entry { | ||||||
|  |  | ||||||
| @@ -93,7 +88,6 @@ public class DnsOverwriteConfig { | |||||||
|         this.homeserver = homeserver; |         this.homeserver = homeserver; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @PostConstruct |  | ||||||
|     public void build() { |     public void build() { | ||||||
|         Gson gson = GsonUtil.build(); |         Gson gson = GsonUtil.build(); | ||||||
|         log.info("--- DNS Overwrite config ---"); |         log.info("--- DNS Overwrite config ---"); | ||||||
|   | |||||||
| @@ -21,14 +21,9 @@ | |||||||
| package io.kamax.mxisd.config; | package io.kamax.mxisd.config; | ||||||
|  |  | ||||||
| import org.apache.commons.lang3.StringUtils; | import org.apache.commons.lang3.StringUtils; | ||||||
| import org.springframework.boot.context.properties.ConfigurationProperties; |  | ||||||
| import org.springframework.context.annotation.Configuration; |  | ||||||
|  |  | ||||||
| import javax.annotation.PostConstruct; |  | ||||||
| import java.util.*; | import java.util.*; | ||||||
|  |  | ||||||
| @Configuration |  | ||||||
| @ConfigurationProperties("exec") |  | ||||||
| public class ExecConfig { | public class ExecConfig { | ||||||
|  |  | ||||||
|     public class IO { |     public class IO { | ||||||
| @@ -516,8 +511,7 @@ public class ExecConfig { | |||||||
|         this.profile = profile; |         this.profile = profile; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @PostConstruct |     public ExecConfig build() { | ||||||
|     public ExecConfig compute() { |  | ||||||
|         if (Objects.isNull(getAuth().isEnabled())) { |         if (Objects.isNull(getAuth().isEnabled())) { | ||||||
|             getAuth().setEnabled(isEnabled()); |             getAuth().setEnabled(isEnabled()); | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -20,27 +20,12 @@ | |||||||
|  |  | ||||||
| package io.kamax.mxisd.config; | package io.kamax.mxisd.config; | ||||||
|  |  | ||||||
| import io.kamax.mxisd.auth.provider.AuthenticatorProvider; |  | ||||||
| import io.kamax.mxisd.backend.firebase.GoogleFirebaseAuthenticator; |  | ||||||
| import io.kamax.mxisd.backend.firebase.GoogleFirebaseProvider; |  | ||||||
| import io.kamax.mxisd.lookup.provider.IThreePidProvider; |  | ||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
| import org.springframework.beans.factory.annotation.Autowired; |  | ||||||
| import org.springframework.boot.context.properties.ConfigurationProperties; |  | ||||||
| import org.springframework.context.annotation.Bean; |  | ||||||
| import org.springframework.context.annotation.Configuration; |  | ||||||
|  |  | ||||||
| import javax.annotation.PostConstruct; |  | ||||||
|  |  | ||||||
| @Configuration |  | ||||||
| @ConfigurationProperties("firebase") |  | ||||||
| public class FirebaseConfig { | public class FirebaseConfig { | ||||||
|  |  | ||||||
|     private Logger log = LoggerFactory.getLogger(FirebaseConfig.class); |     private transient final Logger log = LoggerFactory.getLogger(FirebaseConfig.class); | ||||||
|  |  | ||||||
|     @Autowired |  | ||||||
|     private MatrixConfig mxCfg; |  | ||||||
|  |  | ||||||
|     private boolean enabled; |     private boolean enabled; | ||||||
|     private String credentials; |     private String credentials; | ||||||
| @@ -70,7 +55,6 @@ public class FirebaseConfig { | |||||||
|         this.database = database; |         this.database = database; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @PostConstruct |  | ||||||
|     public void build() { |     public void build() { | ||||||
|         log.info("--- Firebase configuration ---"); |         log.info("--- Firebase configuration ---"); | ||||||
|         log.info("Enabled: {}", isEnabled()); |         log.info("Enabled: {}", isEnabled()); | ||||||
| @@ -80,14 +64,4 @@ public class FirebaseConfig { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Bean |  | ||||||
|     public AuthenticatorProvider getAuthProvider() { |  | ||||||
|         return new GoogleFirebaseAuthenticator(enabled, credentials, database); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Bean |  | ||||||
|     public IThreePidProvider getLookupProvider() { |  | ||||||
|         return new GoogleFirebaseProvider(enabled, credentials, database, mxCfg.getDomain()); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -20,14 +20,9 @@ | |||||||
|  |  | ||||||
| package io.kamax.mxisd.config; | package io.kamax.mxisd.config; | ||||||
|  |  | ||||||
| import org.springframework.boot.context.properties.ConfigurationProperties; |  | ||||||
| import org.springframework.context.annotation.Configuration; |  | ||||||
|  |  | ||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
| import java.util.List; | import java.util.List; | ||||||
|  |  | ||||||
| @Configuration |  | ||||||
| @ConfigurationProperties(prefix = "forward") |  | ||||||
| public class ForwardConfig { | public class ForwardConfig { | ||||||
|  |  | ||||||
|     private List<String> servers = new ArrayList<>(); |     private List<String> servers = new ArrayList<>(); | ||||||
| @@ -40,4 +35,8 @@ public class ForwardConfig { | |||||||
|         this.servers = servers; |         this.servers = servers; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     public void build() { | ||||||
|  |         // no-op | ||||||
|  |     } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user