Compare commits

...

51 Commits

Author SHA1 Message Date
Max Dor
6d1c6ed109 Last cosmetic changes for v1.3.0 2019-02-10 20:41:40 +01:00
Max Dor
1619f5311c Add email verification notification test (/requestToken) 2019-02-09 15:18:06 +01:00
Max Dor
6fa36ea092 Add missing header 2019-02-07 01:39:10 +01:00
Max Dor
471e06536b Improve logging 2019-02-07 01:35:43 +01:00
Max Dor
3a6b75996c Use a proper HTTP client when discovering federated IS to avoid 4xx's 2019-02-06 23:23:40 +01:00
Max Dor
566e4f3137 Correctly handle 3PID notification revamping (forgotten code) 2019-02-06 22:27:42 +01:00
Max Dor
a4c18dee5d Handle possibly trailing slashes for older versions of mxisd 2019-02-06 19:55:22 +01:00
Max Dor
8d6850d346 Link to targeted setups in main README 2019-02-06 04:03:33 +01:00
Max Dor
67bc18af7d Improve docs 2019-02-06 03:53:42 +01:00
Max Dor
5c660fdcaf Add forgotten CORS headers from Spring port 2019-02-05 19:09:47 +01:00
Max Dor
fbbafeb769 Cache processing of bulk lookups and de-dup concurrent requests 2019-02-04 06:04:39 +01:00
Max Dor
559f6a7401 Fix docs 2019-02-04 06:03:15 +01:00
Max Dor
3bebb33147 Revamp 3PID sessions
- Fix #93
- Fix #98
2019-02-04 05:26:33 +01:00
Max Dor
3e240fe34d Improve fraudulent unbind notification 2019-02-01 15:41:44 +01:00
Max Dor
635f6fdbe7 Implementation for blocking fraudulent 3PID /unbind attempts 2019-02-01 02:34:52 +01:00
Max Dor
4237eeb3b6 Skeleton for blocking fraudulent 3PID /unbind attempts 2019-01-30 00:29:51 +01:00
Max Dor
a0e91e7896 Use proper return codes for session errors 2019-01-30 00:28:55 +01:00
Max Dor
aab0b86646 Talk about projects using mxisd under the hood 2019-01-23 18:50:00 +01:00
Max Dor
3e22301af7 Properly handle /v1/store-invite 2019-01-16 02:57:40 +01:00
Max Dor
2b202323c0 Catch and handle more exceptions in Base HTTP handler 2019-01-16 02:57:40 +01:00
Max Dor
4ec05f518e Properly handle v1 of 3pid/bind 2019-01-16 02:57:40 +01:00
Max Dor
6da68298b0 Fix invalid paths 2019-01-16 02:57:40 +01:00
Max Dor
aecaafdeca Set theme jekyll for github pages 2019-01-16 01:31:21 +01:00
Max Dor
d885932f45 Fix loading failures of JDBC drivers for SQL-based Identity stores 2019-01-15 06:22:03 +01:00
Max Dor
c689a3f161 Fix classpath resources config 2019-01-13 00:30:52 +01:00
Max Dor
7805112548 Fix #110 2019-01-11 23:07:58 +01:00
Max Dor
3e89f0bc5e Fix #109 2019-01-11 23:07:52 +01:00
Max Dor
c6b8f7d48e Better handle of File reading / Input Streams 2019-01-11 23:02:57 +01:00
Max Dor
83377ebee0 Protect against NPE 2019-01-11 22:08:35 +01:00
Max Dor
2aa6e4d142 Fix missing .html from Spring to Undertow port 2019-01-11 22:08:22 +01:00
Max Dor
82a1a3df68 Fix invalid parsing of 3PID medium configs 2019-01-11 21:44:51 +01:00
Max Dor
7ec11ba8cf Use NetIQ config for NetIQ identity store instead of generic LDAP one 2019-01-07 04:32:12 +01:00
Max Dor
9317c11434 Use sane handler for all endpoints 2019-01-07 04:25:29 +01:00
Max Dor
b257a0275f Properly handle signing Key ID format 2019-01-07 04:19:53 +01:00
Max Dor
2aaa04062f Fix tests 2019-01-07 03:13:12 +01:00
Max Dor
54c3014568 Port distributions and start scripts to Undertow 2019-01-07 03:01:46 +01:00
Max Dor
c3ca73f576 Port documentation about Thymeleaf 2019-01-07 03:01:46 +01:00
Max Dor
4185b644b7 Continue structural port from Spring Boot to Undertow
- Configuration options
- Configuration documentation
2019-01-07 03:01:46 +01:00
Max Dor
ace5918342 Continue structural port from Spring Boot to Undertow
- Notification template generator
- Add tests for email notification handler
2019-01-07 03:01:46 +01:00
Max Dor
7ad985fead Continue structural port from Spring Boot to Undertow 2019-01-07 03:01:46 +01:00
Max Dor
6a376db322 Formatting (no-op) 2019-01-07 03:01:46 +01:00
Max Dor
950f7c931c Be consistent about testing package 2019-01-07 03:01:46 +01:00
Max Dor
d160a44509 Port default configuration values 2019-01-07 03:01:46 +01:00
Max Dor
05493da27c Start structural port from Spring Boot to Undertow 2019-01-07 03:01:46 +01:00
Max Dor
df44428a85 Fix #106 2019-01-04 19:26:45 +01:00
Max Dor
e6f9c30611 Add support for multiple Base DNs in LDAP Identity Store (Fix #104) 2018-12-23 00:06:15 +01:00
Max Dor
06b2c787d3 Remove unused reference 2018-12-22 04:03:44 +01:00
Max Dor
5645f69208 Add better support for AS transactions (Fix #97)
- Process transactions async with completion parking
- Detect transactions deduplication
2018-12-22 03:52:02 +01:00
Max Dor
92cf5c6b21 Add support for Profile feature in REST Identity store (Fix #91) 2018-12-21 19:21:15 +01:00
Max Dor
ad1b91f370 Proper HTTP encoding for username rewrite 2018-12-21 16:48:29 +01:00
Max Dor
e9c29f1c03 Add support for username rewrite (Fix #103) 2018-12-21 14:22:51 +01:00
309 changed files with 7931 additions and 4700 deletions

1
.gitignore vendored
View File

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

View File

@@ -14,4 +14,5 @@ ENV SQLITE_DATABASE_PATH="/var/mxisd/mxisd.db"
CMD [ "/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

View File

@@ -8,18 +8,20 @@ mxisd - Federated Matrix Identity Server
- [Getting Started](#getting-started)
- [Support](#support)
- [Contribute](#contribute)
- [Powered by mxisd](#powered-by-mxisd)
- [FAQ](#faq)
- [Contact](#contact)
# Overview
mxisd is a Federated Matrix Identity server for self-hosted Matrix infrastructures with [enhanced features](#features).
As an enhanced Identity service, it implements the [Matrix Identity service API](https://kamax.io/matrix/api/identity_service/unstable.html)
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.
It is the one stop shop for anything regarding Authentication, Directory and Identity management in Matrix built in a
single coherent product.
mxisd is specifically designed to connect to an existing on-premise Identity store (AD/Samba/LDAP, SQL Database,
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
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)**.
# 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
([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.)
([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
([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
([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:
- [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
- 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
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.
- 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
See the [dedicated document](docs/faq.md)

View File

@@ -21,9 +21,11 @@
import java.util.regex.Pattern
apply plugin: 'java'
apply plugin: 'org.springframework.boot'
apply plugin: 'application'
apply plugin: 'com.github.johnrengelman.shadow'
apply plugin: 'idea'
def confFileName = "application.example.yaml"
def confFileName = "mxisd.example.yaml"
def distDir = "${project.buildDir}/dist"
def debBinPath = "/usr/lib/mxisd"
@@ -31,7 +33,8 @@ def debConfPath = "/etc/mxisd"
def debDataPath = "/var/lib/mxisd"
def debSystemdPath = "/etc/systemd/system"
def debConfFileName = "mxisd-sample.yaml"
def debConfFileName = confFileName
def debStartScriptFilename = "mxisd"
def debBuildBasePath = "${project.buildDir}/tmp/debian"
def debBuildDebianPath = "${debBuildBasePath}/DEBIAN"
@@ -43,6 +46,9 @@ def debBuildSystemdPath = "${debBuildBasePath}${debSystemdPath}"
def dockerImageName = "kamax/mxisd"
def dockerImageTag = "${dockerImageName}:${mxisdVersion()}"
group = 'io.kamax'
mainClassName = 'io.kamax.mxisd.MxisdStandaloneExec'
String mxisdVersion() {
def versionPattern = Pattern.compile("v(\\d+\\.)?(\\d+\\.)?(\\d+)(-.*)?")
@@ -59,38 +65,41 @@ String gitVersion() {
commandLine = ['git', 'describe', '--tags', '--always', '--dirty']
standardOutput = out
}
return out.toString().replace(System.lineSeparator(), '');
return out.toString().replace(System.lineSeparator(), '')
}
buildscript {
repositories {
mavenCentral()
jcenter()
}
dependencies {
classpath 'org.springframework.boot:spring-boot-gradle-plugin:1.5.3.RELEASE'
classpath 'com.github.jengelman.gradle.plugins:shadow:4.0.3'
}
}
repositories {
mavenCentral()
jcenter()
maven { url "https://kamax.io/maven/releases/" }
maven { url "https://kamax.io/maven/snapshots/" }
}
dependencies {
// Logging
compile 'org.slf4j:slf4j-simple:1.7.25'
// Easy file management
compile 'commons-io:commons-io:2.5'
// Spring Boot - standalone app
compile 'org.springframework.boot:spring-boot-starter-web:1.5.10.RELEASE'
// Thymeleaf for HTML templates
compile "org.springframework.boot:spring-boot-starter-thymeleaf:1.5.10.RELEASE"
// Config management
compile 'org.yaml:snakeyaml:1.23'
// Matrix Java SDK
compile 'io.kamax:matrix-java-sdk:0.0.14-8-g0e57ec6'
// ORMLite
compile 'com.j256.ormlite:ormlite-jdbc:5.0'
// ed25519 handling
compile 'net.i2p.crypto:eddsa:0.1.0'
@@ -107,15 +116,12 @@ dependencies {
compile 'com.googlecode.libphonenumber:libphonenumber:8.7.1'
// E-mail sending
compile 'com.sun.mail:javax.mail: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
compile 'com.google.firebase:firebase-admin:5.3.0'
// ORMLite
compile 'com.j256.ormlite:ormlite-jdbc:5.0'
// Connection Pool
compile 'com.mchange:c3p0:0.9.5.2'
@@ -123,7 +129,7 @@ dependencies {
compile 'org.xerial:sqlite-jdbc:3.20.0'
// PostgreSQL
compile 'org.postgresql:postgresql:42.1.4'
compile 'org.postgresql:postgresql:42.2.5'
// MariaDB/MySQL
compile 'org.mariadb.jdbc:mariadb-java-client:2.1.2'
@@ -137,36 +143,28 @@ dependencies {
// ZT-Exec for exec identity store
compile 'org.zeroturnaround:zt-exec:1.10'
// HTTP server
compile 'io.undertow:undertow-core:2.0.16.Final'
testCompile 'junit:junit:4.12'
testCompile 'com.github.tomakehurst:wiremock:2.8.0'
testCompile 'com.unboundid:unboundid-ldapsdk:4.0.9'
testCompile 'com.icegreen:greenmail:1.5.9'
}
springBoot {
executable = true
embeddedLaunchScriptProperties = [
confFolder: "/etc/default"
]
shadowJar {
baseName = project.name
classifier = null
version = null
}
processResources {
task debBuild(dependsOn: shadowJar) {
doLast {
copy {
from('build/resources/main/application.yaml') {
rename 'application.yaml', 'mxisd.yaml'
}
into 'build/resources/main'
}
}
}
task buildDeb(dependsOn: build) {
doLast {
def v = mxisdVersion()
println "Version for package: ${v}"
String debVersion = mxisdVersion()
println "Version for package: ${debVersion}"
mkdir distDir
mkdir debBuildBasePath
mkdir "${debBuildBasePath}/DEBIAN"
mkdir debBuildDebianPath
mkdir debBuildBinPath
mkdir debBuildConfPath
mkdir debBuildDataPath
@@ -177,10 +175,10 @@ task buildDeb(dependsOn: build) {
into debBuildBinPath
}
ant.chmod(
file: "${debBuildBinPath}/mxisd.jar",
perm: 'a+x'
)
copy {
from "${project.file("src/script/" + debStartScriptFilename)}"
into debBuildBinPath
}
copy {
from(project.file(confFileName)) {
@@ -189,16 +187,16 @@ task buildDeb(dependsOn: build) {
into debBuildConfPath
}
ant.replaceregexp(
ant.replaceregexp( // FIXME adapt to new config format
file: "${debBuildConfPath}/${debConfFileName}",
match: "key.path:(.*)",
replace: "key.path: '${debDataPath}/signing.key'"
match: "key:\\R path:(.*)",
replace: "key:\n path: '${debDataPath}/signing.key'"
)
ant.replaceregexp(
ant.replaceregexp( // FIXME adapt to new config format
file: "${debBuildConfPath}/${debConfFileName}",
match: "storage.provider.sqlite.database:(.*)",
replace: "storage.provider.sqlite.database: '${debDataPath}/mxisd.db'"
match: "storage:\\R provider:\\R sqlite:\\R database:(.*)",
replace: "storage:\n provider:\n sqlite:\n database: '${debDataPath}/mxisd.db'"
)
copy {
@@ -209,7 +207,7 @@ task buildDeb(dependsOn: build) {
ant.replace(
file: "${debBuildDebianPath}/control",
token: 'Version: 0',
value: "Version: ${v}"
value: "Version: ${debVersion}"
)
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
doLast {

View File

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

View File

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

View File

@@ -17,9 +17,8 @@ cd mxisd
./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).
**NOTE**: `application.yaml` is also called `mxisd.yaml` in some specific installations.
Start the server in foreground to validate the build and configuration:
```bash
@@ -29,12 +28,14 @@ java -jar build/libs/mxisd.jar
Ensure the signing key is available:
```bash
$ curl 'http://localhost:8090/_matrix/identity/api/v1/pubkey/ed25519:0'
{"public_key":"..."}
```
Test basic recursive lookup (requires Internet connection with access to TCP 443):
```bash
$ curl 'http://localhost:8090/_matrix/identity/api/v1/lookup?medium=email&address=mxisd-federation-test@kamax.io'
{"address":"mxisd-federation-test@kamax.io","medium":"email","mxid":"@mxisd-lookup-test:kamax.io",...}
```
@@ -57,7 +58,7 @@ Requirements:
[Build mxisd](#build) then:
```bash
./gradlew buildDeb
./gradlew debBuild
```
You will find the debian package in `build/dist`.
Then follow the instruction in the [Debian package](install/debian.md) document.

View File

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

View File

@@ -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
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?
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
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
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.
### I already use the synapse LDAP3 auth provider. Why should I care about mxisd?
The [synapse LDAP3 auth provider](https://github.com/matrix-org/matrix-synapse-ldap3) is not longer maintained and
only handles on specific flow: validate credentials at login.
The [synapse LDAP3 auth provider](https://github.com/matrix-org/matrix-synapse-ldap3) is not longer maintained despite
saying so and only handles on specific flow: validate credentials at login.
It does not:
- Auto-provision user profiles
- Integrate with Identity management
- Integrate with Directory searches
- Integrate with Profile data
mxisd is a replacement and enhancement of it, offering coherent results in all areas, which the LDAP3 auth provider
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
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!
mxisd primary concerns are your privacy and to always be compatible with the Matrix ecosystem and the Identity service API.

View File

@@ -74,7 +74,15 @@ See your Identity store [documentation](../stores/README.md) on how to enable th
## Advanced
The Authentication feature allows users to login to their Homeserver by using their 3PIDs in a configured Identity store.
The Authentication feature allows users to:
- Rewrite usernames matching a pattern to be mapped to another username via a 3PID.
- login to their Homeserver by using their 3PIDs in a configured Identity store.
This feature also allows to work around the following issues:
- Lowercase all usernames for synapse, allowing case-insensitive login
- Unable to login on synapse if username is numerical
- Any generic transformation of username prior to sending to synapse, bypassing the restriction that password providers
cannot change the localpart being authenticated.
### Overview
This is performed by intercepting the Homeserver endpoint `/_matrix/client/r0/login` as depicted below:
@@ -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.
### Requirements
- [Basic Authentication configured and working](#basic)
- Reverse proxy setup
- Homeserver
- Compatible [Identity store](../stores/README.md)
- [Basic Authentication configured and working](#basic)
- Client and Homeserver using the [C2S API r0.4.x](https://matrix.org/docs/spec/client_server/r0.4.0.html) or later
- Reverse proxy setup
### Configuration
#### Reverse Proxy
@@ -143,7 +151,10 @@ 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:
```yaml
dns.overwrite.homeserver.client:
dns:
overwrite:
homeserver:
client:
- name: 'example.org'
value: 'http://localhost:8008'
```
@@ -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.
`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'
```

View File

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

View File

@@ -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:
```yaml
dns.overwrite.homeserver.client:
dns:
overwrite:
homeserver:
client:
- name: 'example.org'
value: 'http://localhost:8008'
```
`name` must be the hostname of the URL that clients use when connecting to the Homeserver.
In case the hostname is the same as your Matrix domain, you can use `${matrix.domain}` to auto-populate the value using
the `matrix.domain` configuration option and avoid duplicating it.
`value` is the base internal URL of the Homeserver, without any `/_matrix/..` or trailing `/`.
- `name` must be the hostname of the URL that clients use when connecting to the Homeserver.
- `value` is the base internal URL of the Homeserver, without any `/_matrix/..` or trailing `/`.
## Next steps
### Homeserver results
You can configure if the Homeserver should be queried at all when doing a directory search.
To disable Homeserver results, set the following in mxisd configuration file:
```yaml
directory.exclude.homeserver: true
directory:
exclude:
homeserver: true
```
### 3PID exclusion in search
You can configure if the 3PID should also be included when doing a directory search.
By default, a search is performed on the 3PIDs. If you would like to not include them:
```yaml
directory.exclude.threepid: true
directory:
exclude:
threepid: true
```

View File

@@ -1,12 +1,16 @@
# Profile enhancement
**WARNING**: Alpha feature, not officially supported. Do not use.
# Profile
**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
applications to retrieve custom data not currently provided by synapse, per example.
## Public Profile enhancement
This feature allows to enhance a public profile query with more info than just Matrix ID and Display name, allowing for
custom applications to retrieve custom data not currently provided by synapse, per example.
## Configuration
### Reverse proxy
#### Apache
**WARNING**: This information can be queried without authentication as per the specification. Do not enable unless in a
controlled environment.
### Configuration
#### Reverse proxy
##### Apache
```apache
ProxyPassMatch "^/_matrix/client/r0/profile/([^/]+)$" "http://127.0.0.1:8090/_matrix/client/r0/profile/$1"
```

View File

@@ -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,
use the following mxisd configuration options:
```yaml
lookup.recursive.enabled: false
invite.resolution.recursive: false
session.policy.validation.forLocal.toRemote.enabled: false
session.policy.validation.forRemote.toRemote.enabled: false
lookup:
recursive:
enabled: false
invite:
resolution:
recursive: false
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.

View File

@@ -1,13 +1,14 @@
# 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
If you would like to use the central matrix.org Identity server to ensure maximum discovery at the cost of potentially
leaking all your contacts information, add the following to your configuration:
```yaml
forward.servers:
forward:
servers:
- 'matrix-org'
```
**NOTE:** You should carefully consider enabling this option, which is discouraged.

10
docs/features/profile.md Normal file
View 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).

View File

@@ -6,22 +6,34 @@
5. [Validate](#validate)
6. [Next steps](#next-steps)
Following these quick start instructions, you will have a basic setup that can perform recursive/federated lookups and
talk to the central Matrix.org Identity server.
Following these quick start instructions, you will have a basic setup that can perform recursive/federated lookups.
This will be a good ground work for further integration with features and your existing Identity stores.
---
If you would like a more fully integrated setup out of the box, the [matrix-docker-ansible-deploy](https://github.com/spantaleev/matrix-docker-ansible-deploy)
project provides a turn-key full-stack solution, including LDAP and the various 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
You will need:
- Working Homeserver, ideally with working federation
- 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
not support HTTPS listener at this time.
If you use synapse:
- It requires an HTTPS connection when talking to an Identity service, **a reverse proxy is required** as 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
hostname.
host.
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
@@ -38,14 +50,14 @@ Install via:
See the [Latest release](https://github.com/kamax-matrix/mxisd/releases/latest) for links to each.
## Configure
**NOTE**: please view the install instruction for your platform, as this step might be optional or already handled for you.
> **NOTE**: Please view the install instruction for your platform, as this step might be optional or already handled for you.
Create/edit a minimal configuration (see installer doc for the location):
```yaml
matrix.domain: 'example.org'
key.path: '/path/to/signing.key.file'
storage.provider.sqlite.database: '/path/to/mxisd.db'
```
> **NOTE**: Details about configuration syntax and format are described [here](configure.md)
If you haven't created a configuration file yet, copy `mxisd.example.yaml` to where the configuration file is stored given
your installation method and edit to your needs.
The following items must be at least configured:
- `matrix.domain` should be set to your Homeserver domain (`server_name` in synapse configuration)
- `key.path` will store the signing keys, which must be kept safe! If the file does not exist, keys will be generated for you.
- `storage.provider.sqlite.database` is the location of the SQLite Database file which will hold state (invites, etc.)
@@ -67,9 +79,9 @@ ProxyPass /_matrix/identity http://0.0.0.0:8090/_matrix/identity
Typical configuration would look like:
```apache
<VirtualHost *:443>
ServerName example.org
ServerName matrix.example.org
...
# ...
ProxyPreserveHost on
ProxyPass /_matrix/identity http://localhost:8090/_matrix/identity
@@ -91,9 +103,9 @@ Typical configuration would look like:
```nginx
server {
listen 443 ssl;
server_name example.org;
server_name matrix.example.org;
...
# ...
location /_matrix/identity {
proxy_pass http://localhost:8090/_matrix/identity;
@@ -114,17 +126,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:
```yaml
trusted_third_party_id_servers:
- example.org
- matrix.example.org
```
It is recommended to remove `matrix.org` and `vector.im` (or any other default entry) from your configuration so only
your own Identity server is authoritative for your HS.
It is **highly recommended** to remove `matrix.org` and `vector.im` (or any other default entry) from your configuration
so only your own Identity server is authoritative for your HS.
## Validate
**NOTE:** In case your homeserver has no working federation, step 5 will not happen. If step 4 took place, consider
your installation validated.
1. Log in using your Matrix client and set `https://example.org` as your Identity server URL, replacing `example.org` by
the relevant hostname which you configured in your reverse proxy.
1. Log in using your Matrix client and set `https://matrix.example.org` as your Identity server URL, replacing `matrix.example.org`
by the relevant hostname which you configured in your reverse proxy.
2. Create a new empty room. All further actions will take place in this room.
3. Invite `mxisd-federation-test@kamax.io`
4. The 3PID invite should be turned into a Matrix invite to `@mxisd-lookup-test:kamax.io`.

View File

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

View File

@@ -39,7 +39,7 @@
| [Authentication](../features/authentication.md) | Yes |
| [Directory](../features/directory.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.
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
```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.
@@ -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:
```yaml
exec.token.<token>: '<value>'
exec:
token:
<token>: '<value>'
```
---
@@ -184,16 +187,19 @@ The following types are available:
### Examples
#### Basic
```yaml
exec.auth.enabled: true
exec.auth.command: '/opt/mxisd-exec/auth.sh'
exec.auth.args: ['{localpart}']
exec.auth.input.type: 'plain'
exec.auth.input.template: '{password}'
exec.auth.env:
exec:
auth:
enabled: true
command: '/opt/mxisd-exec/auth.sh'
args: ['{localpart}']
input:
type: 'plain'
template: '{password}'
env:
DOMAIN: '{domain}'
```
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 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`
- Failure exit status of `1`
- Any other exit status considered as error
- The standard output processing as not processed
- Standard output will not be processed
#### Advanced
Given the fictional `placeholder` feature:
@@ -243,14 +249,17 @@ See each dedicated [Feature](#features) section.
## Authentication
The Authentication feature can be enabled/disabled using:
```yaml
exec.auth.enabled: <true/false>
exec:
auth:
enabled: <true/false>
```
---
This feature provides a single *Executable* under the namespace:
```yaml
exec.auth:
exec:
auth:
...
```
@@ -294,7 +303,9 @@ Default template:
## Directory
The Directory feature can be enabled/disabled using:
```yaml
exec.directory.enabled: <true/false>
exec:
directory:
enabled: <true/false>
```
---
@@ -303,12 +314,18 @@ Two search types configuration namespace are available, using the same input/out
By name:
```yaml
exec.directory.search.byName:
exec:
directory:
search:
byName:
...
```
By 3PID:
```yaml
exec.directory.search.byThreepid:
exec:
directory:
search:
byThreepid:
...
```
@@ -386,7 +403,10 @@ The User ID type will default to `localpart` if:
### Bulk lookup
Configuration namespace:
```yaml
exec.identity.lookup.bulk:
exec:
identity:
lookup:
bulk:
...
```
@@ -418,7 +438,9 @@ Same as the [REST Identity Store](rest.md).
## Profile
The Profile feature can be enabled/disabled using:
```yaml
exec.profile.enabled: <true/false>
exec:
profile:
enabled: <true/false>
```
---
@@ -427,19 +449,25 @@ The following *Executable*s namespace are available, share the same input/output
Get Display name:
```yaml
exec.profile.displayName:
exec:
profile:
displayName:
...
```
Get 3PIDs:
```yaml
exec.profile.threePid:
exec:
profile:
threePid:
...
```
Get Roles:
```yaml
exec.profile.role:
exec:
profile:
role:
...
```

View File

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

View File

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

View File

@@ -3,38 +3,45 @@ The REST backend allows you to query identity data in existing webapps, like:
- Forums (phpBB, Discourse, etc.)
- Custom Identity stores (Keycloak, ...)
- CRMs (Wordpress, ...)
- self-hosted clouds (Nextcloud, ownCloud, ...)
- Self-hosted clouds (Nextcloud, ownCloud, ...)
To integrate this backend with your webapp, you will need to implement three specific REST endpoints detailed below.
To integrate this backend with your webapp, you will need to implement the REST endpoints described below.
## Features
| Name | Supported? |
|----------------|------------|
| Authentication | Yes |
| Directory | Yes |
| Identity | Yes |
| Profile | No |
|-------------------------------------------------|------------|
| [Authentication](../features/authentication.md) | Yes |
| [Directory](../features/directory.md) | Yes |
| [Identity](../features/identity.md) | Yes |
| [Profile](../features/profile.md) | Yes |
## Configuration
| Key | Default | Description |
|----------------------------------|------------------------------------------------|------------------------------------------------------|
|--------------------------------------|------------------------------------------------|------------------------------------------------------|
| `rest.enabled` | `false` | Globally enable/disable the REST backend |
| `rest.host` | *None* | Default base URL to use for the different endpoints. |
| `rest.endpoints.auth` | `/_mxisd/backend/api/v1/auth/login` | Validate credentials and get user profile |
| `rest.endpoints.directory` | `/_mxisd/backend/api/v1/directory/user/search` | Search for users by arbitrary input |
| `rest.endpoints.identity.single` | `/_mxisd/backend/api/v1/identity/single` | Endpoint to query a single 3PID |
| `rest.endpoints.identity.bulk` | `/_mxisd/backend/api/v1/identity/bulk` | Endpoint to query a list of 3PID |
| `rest.endpoints.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:
- URL Path starting with `/` that gets happened to the `rest.host`
- Full URL, if you want each endpoint to go to a specific server/protocol/port
If an endpoint value is configured as an empty string, it will disable that specific feature, essentially bypassing the
Identity store for that specific query.
`rest.host` is mandatory if at least one endpoint is not a full URL.
## Endpoints
### Authentication
HTTP method: `POST`
Content-type: JSON UTF-8
- Method: `POST`
- Content-Type: `application/json` (JSON)
- Encoding: `UTF8`
#### Request Body
```json
@@ -87,8 +94,9 @@ If the authentication succeed:
```
### Directory
HTTP method: `POST`
Content-type: JSON UTF-8
- Method: `POST`
- Content-Type: `application/json` (JSON)
- Encoding: `UTF8`
#### Request Body
```json
@@ -113,7 +121,7 @@ If users found:
"user_id": "UserIdLocalpart"
},
{
...
"...": "..."
}
]
}
@@ -129,10 +137,11 @@ If no user found:
### Identity
#### Single 3PID lookup
HTTP method: `POST`
Content-type: JSON UTF-8
- Method: `POST`
- Content-Type: `application/json` (JSON)
- Encoding: `UTF8`
#### Request Body
##### Request Body
```json
{
"lookup": {
@@ -142,7 +151,7 @@ Content-type: JSON UTF-8
}
```
#### Response Body
##### Response Body
If a match was found:
- `lookup.id.type` supported values: `localpart`, `mxid`
```json
@@ -164,10 +173,11 @@ If no match was found:
```
#### Bulk 3PID lookup
HTTP method: `POST`
Content-type: JSON UTF-8
- Method: `POST`
- Content-Type: `application/json` (JSON)
- Encoding: `UTF8`
#### Request Body
##### Request Body
```json
{
"lookup": [
@@ -183,7 +193,7 @@ Content-type: JSON UTF-8
}
```
#### Response Body
##### Response Body
For all entries where a match was found:
- `lookup[].id.type` supported values: `localpart`, `mxid`
```json
@@ -215,3 +225,53 @@ If no match was found:
"lookup": []
}
```
### Profile
#### Request Body
For all requests, the values are the same:
- Method: `POST`
- Content-Type: `application/json` (JSON)
- Encoding: `UTF8`
With body (example values):
##### Request Body
```json
{
"mxid": "@john.doe:example.org",
"localpart": "john.doe",
"domain": "example.org"
}
```
#### Response Body
For all responses, the same object structure will be parsed, making the non-relevant fields as optional.
Structure with example values:
```json
{
"profile": {
"display_name": "John Doe",
"threepids": [
{
"medium": "email",
"address": "john.doe@example.org"
},
{
"...": "..."
}
],
"roles": [
"DomainUsers",
"SalesOrg",
"..."
]
}
}
```
The base `profile` key is mandatory. `display_name`, `threepids` and `roles` are only to be returned on the relevant request.
If there is no profile, the following response is expected:
```json
{
"profile": {}
}
```

View File

@@ -6,28 +6,30 @@
- SQLite
## Features
| Name | Supported? |
|----------------|------------|
| Authentication | No |
| Directory | Yes |
| Identity | Yes |
| Profile | Yes |
| Name | Supported |
|-------------------------------------------------|-----------|
| [Authentication](../features/authentication.md) | No |
| [Directory](../features/directory.md) | Yes |
| [Identity](../features/identity.md) | Yes |
| [Profile](../features/profile.md) | Yes |
Due to the implementation complexity of supporting arbitrary hashing/encoding mechanisms or auth flow, Authentication
will be out of scope of SQL Identity stores and should be done via one of the other identity stores, typically
the [REST Identity store](rest.md).
the [Exec Identity Store](exec.md) or the [REST Identity Store](rest.md).
## Configuration
### Basic
```yaml
sql.enabled: <boolean>
sql:
enabled: <boolean>
```
Enable/disable the identity store
---
```yaml
sql.type: <string>
sql:
type: <string>
```
Set the SQL backend to use:
- `sqlite`
@@ -38,14 +40,16 @@ Set the SQL backend to use:
### Connection
#### SQLite
```yaml
sql.connection: <string>
sql:
connection: <string>
```
Set the value to the absolute path to the Synapse SQLite DB file.
Example: `/path/to/sqlite/file.db`
#### Others
```yaml
sql.connection: //<HOST[:PORT]/DB?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:
- `HOST`: Hostname of the SQL server
@@ -58,14 +62,17 @@ This follow the JDBC URI syntax. See [official website](https://docs.oracle.com/
### Directory
```yaml
sql.directory.enabled: false
sql:
directory:
enabled: false
```
---
```yaml
sql.directory.query:
sql:
directory:
query:
name:
type: <string>
value: <string>
@@ -83,7 +90,9 @@ For each query, `type` can be used to tell mxisd how to process the ID column:
Example:
```yaml
sql.directory.query:
sql:
directory:
query:
name:
type: 'localpart'
value: 'SELECT idColumn, displayNameColumn FROM table WHERE displayNameColumn LIKE ?'
@@ -94,6 +103,8 @@ sql.directory.query:
### Identity
```yaml
sql.identity.type: <string>
sql.identity.query: <string>
sql:
identity:
type: <string>
query: <string>
```

View File

@@ -2,26 +2,28 @@
Synapse's Database itself can be used as an Identity store.
## Features
| Name | Supported? |
|----------------|------------|
| Authentication | No |
| Directory | Yes |
| Identity | Yes |
| Profile | Yes |
| Name | Supported |
|-------------------------------------------------|-----------|
| [Authentication](../features/authentication.md) | No |
| [Directory](../features/directory.md) | Yes |
| [Identity](../features/identity.md) | Yes |
| [Profile](../features/profile.md) | Yes |
Authentication is done by Synapse itself.
## Configuration
### Basic
```yaml
synapseSql.enabled: <boolean>
synapseSql:
enabled: <boolean>
```
Enable/disable the identity store
---
```yaml
synapseSql.type: <string>
synapseSql:
type: <string>
```
Set the SQL backend to use which is configured in synapse:
- `sqlite`
@@ -29,14 +31,16 @@ Set the SQL backend to use which is configured in synapse:
### SQLite
```yaml
synapseSql.connection: <string>
synapseSql:
connection: <string>
```
Set the value to the absolute path to the Synapse SQLite DB file.
Example: `/path/to/synapse/sqliteFile.db`
### PostgreSQL
```yaml
synapseSql.connection: //<HOST[:PORT]/DB?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:
- `HOST`: Hostname of the SQL server

View File

@@ -5,12 +5,12 @@ Two types of connections are required for full support:
- Direct SQL access
## Features
| Name | Supported? |
|----------------|------------|
| Authentication | Yes |
| Directory | Yes |
| Identity | Yes |
| Profile | No |
| Name | Supported |
|-------------------------------------------------|-----------|
| [Authentication](../features/authentication.md) | Yes |
| [Directory](../features/directory.md) | Yes |
| [Identity](../features/identity.md) | Yes |
| [Profile](../features/profile.md) | No |
## Requirements
- [Wordpress](https://wordpress.org/download/) >= 4.4
@@ -34,22 +34,29 @@ If this is not the case for your installation, the mxisd URL will need to be app
### mxisd
Enable in the configuration:
```yaml
wordpress.enabled: true
wordpress:
enabled: true
```
Configure the URL to your Wordpress installation - see above about added `/index.php`:
```yaml
wordpress.rest.base: 'http://localhost:8080'
wordpress:
rest:
base: 'http://localhost:8080'
```
Configure the SQL connection to your Wordpress database:
```yaml
wordpress.sql.connection: '//127.0.0.1/wordpress?user=root&password=example'
wordpress:
sql:
connection: '//127.0.0.1/wordpress?user=root&password=example'
```
---
By default, MySQL database is expected. If you use another database, use:
```yaml
wordpress.sql.type: <string>
wordpress:
sql:
type: <string>
```
With possible values:
- `mysql`
@@ -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:
```yaml
wordpress.sql.tablePrefix: <string>
wordpress:
sql:
tablePrefix: <string>
```
By default, the value is set to `wp_`.

View File

@@ -5,7 +5,9 @@ Connector ID: `smtp`
## Configuration
```yaml
threepid.medium.email:
threepid:
medium:
email:
identity:
from: 'identityServerEmail@example.org'
name: 'My Identity Server'

View File

@@ -5,7 +5,12 @@ Connector ID: `twilio`
## Configuration
```yaml
threepid.medium.msisdn.connectors.twilio.accountSid: 'myAccountSid'
threepid.medium.msisdn.connectors.twilio.authToken: 'myAuthToken'
threepid.medium.msisdn.connectors.twilio.number: '+123456789'
threepid:
medium:
msisdn:
connectors:
twilio:
accountSid: 'myAccountSid'
authToken: 'myAuthToken'
number: '+123456789'
```

View File

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

View File

@@ -1,7 +1,39 @@
# 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
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>
```

View File

@@ -11,14 +11,16 @@ placeholders and also have their own individual set of placeholders.
## Configuration
To configure paths to the various templates:
```yaml
threepid.medium.<YOUR 3PID MEDIUM HERE>:
threepid:
medium:
<YOUR 3PID MEDIUM HERE>:
generators:
template:
invite: '/path/to/invite-template.eml'
session:
validation:
local: '/path/to/validate-local-template.eml'
remote: 'path/to/validate-remote-template.eml'
validation: '/path/to/validate-template.eml'
unbind:
frandulent: '/path/to/unbind-fraudulent-template.eml'
generic:
matrixId: '/path/to/mxid-invite-template.eml'
```

View File

@@ -1,86 +1,34 @@
# Web pages for the 3PID sessions
You can customize the various pages used during a 3PID validation using [Thymeleaf templates](http://www.thymeleaf.org/).
You can customize the various pages used during a 3PID validation using the options below.
## Configuration
Pseudo-configuration to illustrate the structure:
```yaml
# CONFIGURATION EXAMPLE
# DO NOT COPY/PASTE THIS IN YOUR CONFIGURATION
view.session.local:
view:
session:
onTokenSubmit:
success: '/path/to/session/local/tokenSubmitSuccess-page.html'
failure: '/path/to/session/local/tokenSubmitFailure-page.html'
view.session.localRemote:
onTokenSubmit:
success: '/path/to/session/localRemote/tokenSubmitSuccess-page.html'
failure: '/path/to/session/local/tokenSubmitFailure-page.html'
view.session.remote:
onRequest:
success: '/path/to/session/remote/requestSuccess-page.html'
failure: '/path/to/session/remote/requestFailure-page.html'
onCheck:
success: '/path/to/session/remote/checkSuccess-page.html'
failure: '/path/to/session/remote/checkFailure-page.html'
success: '/path/to/session/tokenSubmitSuccess-page.html'
failure: '/path/to/session/tokenSubmitFailure-page.html'
# CONFIGURATION EXAMPLE
# DO NOT COPY/PASTE THIS IN YOUR CONFIGURATION
```
3PID session are divided into three config sections:
- `local` for local-only 3PID sessions
- `localRemote` for local 3PID sessions that can also be turned into remote sessions, if the user so desires
- `remote` for remote-only 3PID sessions
Each section contains a sub-key per support event. Finally, a `success` and `failure` key is available depending on the
outcome of the request.
## Local
### onTokenSubmit
`view.session`:
This is triggered when a user submit a validation token for a 3PID session. It is typically visited when clicking the
link in a validation email.
The template should typically inform the user that the validation was successful and to go back in their Matrix client
to finish the validation process.
to finish the validation process, or that the validation failed.
#### Placeholders
Two configuration keys are available that accept paths to HTML templates:
- `success`
- `failure`
## Placeholders
### Success
No object/placeholder are currently available.
## Local & Remote
### onTokenSubmit
This is triggered when a user submit a validation token for a 3PID session. It is typically visited when clicking the
link in a validation email.
The template should typically inform the user that their 3PID address will not yet be publicly/globally usable. In case
they want to make it, they should start a Remote 3PID session with a given link or that they can go back to their Matrix
client if they do not wish to proceed any further.
#### Placeholders
##### Success
`<a th:href="${remoteSessionLink}">text</a>` can be used to display the link to start a Remote 3PID session.
##### Failure
No object/placeholder are currently available.
## Remote
### onRequest
This is triggered when a user starts a Remote 3PID session, usually from a link produced in the `local.onTokenSubmit`
view or in a remote-only 3PID notification.
The template should typically inform the user that the remote creation was successful, followed the instructions sent by
the remote Identity server and, once that is done, click a link to validate the session.
#### Placeholders
##### Success
`<a th:href="${checkLink}">text</a>` can be used to display the link to validate the Remote 3PID session.
##### Failure
No object/placeholder are currently available.
### onCheck
This is triggered when a user attempts to inform the Identity server that the Remote 3PID session has been validated
with the remote Identity server.
The template should typically inform the user that the validation was successful and to go back in their Matrix client
to finish the validation process.
#### Placeholders
### Failure
No object/placeholder are currently available.

View File

@@ -1,9 +1,8 @@
# 3PID Sessions
- [Overview](#overview)
- [Purpose](#purpose)
- [Federation](#federation)
- [3PID scope](#3pid-scope)
- [Session scope](#session-scope)
- [Restrictions](#restrictions)
- [Bindings](#bindings)
- [Federation](#federation)
- [Notifications](#notifications)
- [Email](#email)
- [Phone numbers](#msisdn-(phone-numbers))
@@ -11,28 +10,39 @@
- [Configuration](#configuration)
- [Web views](#web-views)
- [Scenarios](#scenarios)
- [Default](#default)
- [Local sessions only](#local-sessions-only)
- [Remote sessions only](#remote-sessions-only)
- [Sessions disabled](#sessions-disabled)
## Overview
When adding an email, a phone number or any other kind of 3PID (Third-Party Identifier) in a Matrix client,
the identity server is contacted to validate the 3PID.
To validate the 3PID the identity server sends a message to the 3PID (e.g. an
email) with a hyperlink back to a web-page managed by the identity server to
confirm ownership of the 3PID.
To validate the 3PID, the identity server creates a session associated with a secret token. That token is sent via a message
to the 3PID (e.g. an email) with a the necessary info so the user can submit them to the Identity Server, confirm ownership
of the 3PID.
Once this 3PID is validated, the Homeserver will publish the user Matrix ID on the Identity Server and
add this 3PID to the Matrix account which initiated the request.
Once this 3PID is validated, the Homeserver will request that the Identity Server links the provided user Matrix ID with
the 3PID session and finally add the 3PID to its own data store.
This serves two purposes:
- Add the 3PID as an administrative/login info for the Homeserver directly
- Publish, or *Bind*, the 3PID so it can be queried from Homeservers and clients when inviting someone in a room
- Links, called *Bind*, the 3PID so it can be queried from Homeservers and clients when inviting someone in a room
by a 3PID, allowing it to be resolved to a Matrix ID.
## Federation
## 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.
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`.
Nevertheless, Matrix users might add 3PIDs that are not associated to a domain, for example telephone numbers.
Or they might even add 3PIDs associated to a different domain (such as an email address hosted by gmail).
Such 3PIDs cannot be resolved in a federated way.
Or they might even add 3PIDs associated to a different domain (such as an email address hosted by Gmail).
Such 3PIDs cannot be resolved in a federated way and will not be found from other servers.
Example: a user from Homeserver `example.org` adds an email `john@gmail.com`.
If a federated lookup was performed, Identity servers would try to find the 3PID bind at the `gmail.com` server, and
not `example.org`.
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
- 3PIDs that cannot be resolved locally or using federation, are fowarded to another global identity server.
- 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.
As mxisd is built for self-hosted use cases, mainly for orgs/corps, this is usually not a problem for emails.
Sadly, there is currently no mechanism to make this work for phone numbers.
## Notifications
3PIDs are validated by sending a pre-formatted message containing a token to that 3PID address, which must be given to the
@@ -126,44 +90,36 @@ Connectors:
## Usage
### Configuration
The following example of configuration (incomplete extract) shows which items are relevant for 3PID sessions.
The following example of configuration shows which items are relevant for 3PID sessions.
**IMPORTANT:** Most configuration items shown have default values and should not be included in your own configuration
file unless you want to specifically overwrite them.
```yaml
# CONFIGURATION EXAMPLE
# DO NOT COPY/PASTE THIS IN YOUR CONFIGURATION
session.policy.validation.enabled: true
session.policy.validation.forLocal:
# DO NOT COPY/PASTE AS-IS IN YOUR CONFIGURATION
session:
policy:
validation:
enabled: true
toLocal: true
toRemote:
enabled: true
server: 'configExample' # Not to be included in config! Already present in default config!
session.policy.validation.forRemote:
enabled: true
toLocal: true
toRemote:
enabled: true
server: 'configExample' # Not to be included in config! Already present in default config!
# DO NOT COPY/PASTE THIS IN YOUR CONFIGURATION
unbind:
fraudulent:
sendWarning: true
# DO NOT COPY/PASTE AS-IS IN YOUR CONFIGURATION
# CONFIGURATION EXAMPLE
```
`session.policy.validation` is the core configuration to control what users configured to use your Identity server
are allowed to do in terms of 3PID sessions.
are allowed to do in terms of 3PID sessions. The policy has a global on/off switch for 3PID sessions using `.enabled`
The policy 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:
- global on/off switch for 3PID sessions using `.enabled`
- `toLocal` allowing or not local 3PID session validations
- `toRemote` allowing or not remote 3PID session validations and to which server such sessions should be sent.
`.server` takes a Matrix Identity server list label. Only the first server in the list is currently used.
`unbind.fraudulent` controls warning notifications if an illegal/fraudulent 3PID removal is attempted on the Identity server.
This is directly related to synapse disregard for privacy and new GDPR laws in Europe in an attempt to inform users about
potential privacy leaks.
If both `toLocal` and `toRemote` are enabled, the user will be offered to initiate a remote session once their 3PID
locally validated.
For more information, see the corresponding [synapse issue](https://github.com/matrix-org/synapse/issues/4540).
### Web views
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.
### Scenarios
It is important to keep in mind that mxisd does not create bindings, irrelevant if a user added a 3PID to their profile.
Instead, when queried for bindings, mxisd will query Identity stores which are responsible to store this kind of information.
This has the side effect that any 3PID added to a user profile which is NOT within a configured and enabled Identity backend
will simply not be usable for search or invites, **even on the same Homeserver!**
mxisd does not store binds on purpose, as one of its primary goal is to ensure maximum compatibility with federation
and the rest of the Matrix ecosystem is preserved.
Nonetheless, because mxisd also aims at offering support for tight control over identity data, it is possible to have
such 3PID bindings available for search and invite queries on synapse with the corresponding [Identity store](../../stores/synapse.md).
See the [Local sessions only](#local-sessions-only) use case for more information on how to configure.
#### Default
By default, mxisd allows the following:
| | Local Session | Remote Session |
|-----------------|-------------------|----------------|
| **Local 3PID** | Yes | Yes, offered |
| **Remote 3PID** | No, Remote forced | Yes |
This is usually what people expect and will feel natural to users and does not involve further integration.
This allows to stay in control for e-mail addresses which domain matches your Matrix environment, still making them
discoverable with federation but not recorded in a 3rd party Identity server which is not under your control.
Users still get the possibility to publish globally their address if needed.
Other e-mail addresses and phone number will be redirected to remote sessions to ensure full compatibility with the Matrix
ecosystem and other federated servers.
#### Local sessions only
**NOTE:** This does not affect 3PID lookups (queries to find Matrix IDs). See [Federation](../../features/federation.md)
to disable remote lookup for those.
This configuration ensures maximum confidentiality and privacy.
Typical use cases:
- Private Homeserver, not federated
- Internal Homeserver without direct Internet access
- Custom product based on Matrix which does not federate
No 3PID will be sent to a remote Identity server and all validation will be performed locally.
On the flip side, people with *Remote* 3PID scopes will not be found from other servers.
Use the following values:
```yaml
session.policy.validation.enabled: true
session.policy.validation.forLocal:
enabled: true
toLocal: true
toRemote:
enabled: false
session.policy.validation.forRemote:
enabled: true
toLocal: true
toRemote:
enabled: false
```
**IMPORTANT**: When using local-only mode and if you are using synapse, you will also need to enable its dedicated Identity
store if you want user searches and invites to work. To do so, see the [dedicated document](../../stores/synapse.md).
#### Remote sessions only
This configuration ensures all 3PID are made public for maximum compatibility and reach within the Matrix ecosystem, at
the cost of confidentiality and privacy.
Typical use cases:
- Public Homeserver
- Homeserver with registration enabled
Use the following values:
```yaml
session.policy.validation.enabled: true
session.policy.validation.forLocal:
enabled: true
toLocal: false
toRemote:
enabled: true
session.policy.validation.forRemote:
enabled: true
toLocal: false
toRemote:
enabled: true
```
#### Sessions disabled
This configuration would disable 3PID session altogether, preventing users from adding emails and/or phone numbers to
their profiles.
This configuration would disable 3PID sessions altogether, preventing users from validating emails and/or phone numbers
and any subsequent actions that requires them, like adding them to their profiles.
This would be used if mxisd is also performing authentication for the Homeserver, typically with synapse and the
[REST password provider](https://github.com/kamax-io/matrix-synapse-rest-auth).
**This mode comes with several important restrictions:**
- This 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.
[REST password provider](https://github.com/kamax-matrix/matrix-synapse-rest-auth), where 3PID mappings would be
auto-populated.
Use the following values to enable this mode:
```yaml
session.policy.validation.enabled: false
session:
policy:
validation:
enabled: false
```

View File

@@ -1,50 +1,46 @@
# 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 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.
matrix.domain: ''
# This is used to build the various identifiers in all the features.
matrix:
domain: ''
################
# Signing keys #
################
# 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:
# - /var/opt/mxisd/sign.key
# - /var/local/mxisd/sign.key
# - /var/lib/mxisd/sign.key
#
# The signing key is auto-generated during execution time if not present.
key.path: ''
key:
path: ''
############################
# Persistence config items #
############################
# Configure the storage backend, usually a DB
# Possible built-in values:
# sqlite SQLite backend, default
#
#storage.backend: 'sqlite'
# Path to the SQLite DB file
# Path to the SQLite DB file for mxisd internal storage
#
# Examples:
# - /var/opt/mxisd/mxisd.db
# - /var/local/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.
# 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
#
# 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
#
#### E-mail invite sender
#
# SMTP host
threepid.medium.email.connectors.smtp.host: "smtp.example.org"
threepid:
medium:
email:
identity:
# The e-mail to send as.
from: "matrix-identity@example.org"
# SMTP port
threepid.medium.email.connectors.smtp.port: 587
connectors:
smtp:
# SMTP host
host: "smtp.example.org"
# TLS mode for the connection.
#
# Possible values:
# 0 Disable TLS entirely
# 1 Enable TLS if supported by server (default)
# 2 Force TLS and fail if not available
#
#threepid.medium.email.connectors.smtp.tls: 1
# SMTP port
port: 587
# Login for SMTP
threepid.medium.email.connectors.smtp.login: "matrix-identity@example.org"
# TLS mode for the connection.
#
# Possible values:
# 0 Disable TLS entirely
# 1 Enable TLS if supported by server (default)
# 2 Force TLS and fail if not available
#
tls: 1
# Password for the account
threepid.medium.email.connectors.smtp.password: "ThePassword"
# Login for SMTP
login: "matrix-identity@example.org"
# The e-mail to send as.
threepid.medium.email.identity.from: "matrix-identity@example.org"
# Password for the account
password: "ThePassword"

View File

@@ -6,8 +6,8 @@ useradd -r mxisd || true
# Set permissions for data directory
chown -R mxisd:mxisd %DEB_DATA_DIR%
# Create symlink to mxusd
ln -sfT /usr/lib/mxisd/mxisd.jar /usr/bin/mxisd
# Create symlink to mxisd run script
ln -sfT /usr/lib/mxisd/mxisd /usr/bin/mxisd
# Enable systemd service
systemctl enable mxisd.service

View File

@@ -1,25 +1,34 @@
#!/usr/bin/env bash
#!/bin/bash
if [[ -n "$CONF_FILE_PATH" ]] && [ ! -f "$CONF_FILE_PATH" ]; then
echo "Generating config file $CONF_FILE_PATH"
touch "CONF_FILE_PATH"
if [[ -n "$MATRIX_DOMAIN" ]]; then
echo "Setting matrix domain to $MATRIX_DOMAIN"
echo "matrix.domain: $MATRIX_DOMAIN" >> "$CONF_FILE_PATH"
echo "matrix:" >> "$CONF_FILE_PATH"
echo " domain: '$MATRIX_DOMAIN'" >> "$CONF_FILE_PATH"
echo >> "$CONF_FILE_PATH"
fi
if [[ -n "$SIGN_KEY_PATH" ]]; then
echo "Setting signing key path to $SIGN_KEY_PATH"
echo "key.path: $SIGN_KEY_PATH" >> "$CONF_FILE_PATH"
echo "key:" >> "$CONF_FILE_PATH"
echo " path: '$SIGN_KEY_PATH'" >> "$CONF_FILE_PATH"
echo >> "$CONF_FILE_PATH"
fi
if [[ -n "$SQLITE_DATABASE_PATH" ]]; then
echo "Setting SQLite DB path to $SQLITE_DATABASE_PATH"
echo "storage.provider.sqlite.database: $SQLITE_DATABASE_PATH" >> "$CONF_FILE_PATH"
echo "storage:" >> "$CONF_FILE_PATH"
echo " provider:" >> "$CONF_FILE_PATH"
echo " sqlite:" >> "$CONF_FILE_PATH"
echo " database: '$SQLITE_DATABASE_PATH'" >> "$CONF_FILE_PATH"
echo >> "$CONF_FILE_PATH"
fi
echo "Starting mxisd..."
echo
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

View File

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

View 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();
}
}

View 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
}
}

View 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);
}
}
}

View File

@@ -29,38 +29,119 @@ import io.kamax.matrix.event.EventKey;
import io.kamax.matrix.json.GsonUtil;
import io.kamax.mxisd.backend.sql.synapse.Synapse;
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.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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.io.InputStream;
import java.time.Instant;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
@Component
public class AppServiceHandler {
public class AppSvcManager {
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 IStorage store;
private ProfileManager profiler;
private NotificationManager notif;
private Synapse synapse;
@Autowired
public AppServiceHandler(MatrixConfig cfg, ProfileManager profiler, NotificationManager notif, Synapse synapse) {
this.cfg = cfg;
private Map<String, CompletableFuture<String>> transactionsInProgress;
public AppSvcManager(MxisdConfig cfg, IStorage store, ProfileManager profiler, NotificationManager notif, Synapse synapse) {
this.cfg = cfg.getMatrix();
this.store = store;
this.profiler = profiler;
this.notif = notif;
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) {
log.info("Processing transaction events: start");
eventsJson.forEach(ev -> {
String evId = EventKey.Id.getStringOrNull(ev);
if (StringUtils.isBlank(evId)) {
@@ -78,10 +159,11 @@ public class AppServiceHandler {
String senderId = EventKey.Sender.getStringOrNull(ev);
if (StringUtils.isBlank(senderId)) {
log.debug("Event has no room ID, skipping");
log.debug("Event has no sender ID, skipping");
return;
}
_MatrixID sender = MatrixID.asAcceptable(senderId);
log.debug("Sender: {}", senderId);
if (!StringUtils.equals("m.room.member", GsonUtil.getStringOrNull(ev, "type"))) {
log.debug("This is not a room membership event, skipping");
@@ -105,7 +187,7 @@ public class AppServiceHandler {
return;
}
log.info("Got invite for {}", inviteeId);
log.info("Got invite from {} to {}", senderId, inviteeId);
boolean wasSent = false;
List<_ThreePid> tpids = profiler.getThreepids(invitee).stream()
@@ -121,7 +203,7 @@ public class AppServiceHandler {
synapse.getRoomName(roomId).ifPresent(name -> properties.put("room_name", name));
} catch (RuntimeException 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);
@@ -134,6 +216,8 @@ public class AppServiceHandler {
log.debug("Event {}: processing end", evId);
});
log.info("Processing transaction events: end");
}
}

View File

@@ -20,37 +20,92 @@
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.ThreePid;
import io.kamax.matrix._MatrixID;
import io.kamax.matrix._ThreePid;
import io.kamax.matrix.json.GsonUtil;
import io.kamax.mxisd.UserIdType;
import io.kamax.mxisd.auth.provider.AuthenticatorProvider;
import io.kamax.mxisd.auth.provider.BackendAuthResult;
import io.kamax.mxisd.config.AuthenticationConfig;
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.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.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.List;
import java.util.Objects;
@Service
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 List<AuthenticatorProvider> providers = new ArrayList<>();
private transient final Logger log = LoggerFactory.getLogger(AuthManager.class);
private final Gson gson = GsonUtil.get(); // FIXME replace
@Autowired
private List<AuthenticatorProvider> providers;
private MatrixConfig mxCfg;
@Autowired
private AuthenticationConfig cfg;
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) {
_MatrixID mxid = MatrixID.asAcceptable(id);
@@ -92,4 +147,128 @@ public class AuthManager {
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);
}
}
}

View File

@@ -18,20 +18,25 @@
* 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";
public static final String SESSION_REQUEST_TOKEN = BASE + "/validate/requestToken";
public static final String SESSION_CHECK = BASE + "/validate/check";
import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;
import java.util.stream.Collectors;
public static String getRequestToken(String id, String secret) {
return SESSION_REQUEST_TOKEN + "?sid=" + id + "&client_secret=" + secret;
public class AuthProviders {
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) {
return SESSION_CHECK + "?sid=" + id + "&client_secret=" + secret;
public static List<? extends AuthenticatorProvider> get() {
return suppliers.stream().map(Supplier::get).collect(Collectors.toList());
}
}

View File

@@ -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> {
}

View File

@@ -33,20 +33,16 @@ import io.kamax.mxisd.exception.InternalServerError;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Objects;
import java.util.Optional;
@Component
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;
@Autowired
public ExecAuthStore(ExecConfig cfg) {
this.cfg = Objects.requireNonNull(cfg.getAuth());
}

View File

@@ -24,22 +24,19 @@ import io.kamax.matrix.MatrixID;
import io.kamax.matrix.json.GsonUtil;
import io.kamax.mxisd.config.ExecConfig;
import io.kamax.mxisd.config.MatrixConfig;
import io.kamax.mxisd.controller.directory.v1.io.UserDirectorySearchRequest;
import io.kamax.mxisd.controller.directory.v1.io.UserDirectorySearchResult;
import io.kamax.mxisd.directory.IDirectoryProvider;
import io.kamax.mxisd.config.MxisdConfig;
import io.kamax.mxisd.directory.DirectoryProvider;
import io.kamax.mxisd.http.io.UserDirectorySearchRequest;
import io.kamax.mxisd.http.io.UserDirectorySearchResult;
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 IDirectoryProvider {
public class ExecDirectoryStore extends ExecStore implements DirectoryProvider {
private ExecConfig.Directory cfg;
private MatrixConfig mxCfg;
@Autowired
public ExecDirectoryStore(ExecConfig cfg, MatrixConfig mxCfg) {
this(cfg.getDirectory(), mxCfg);
public ExecDirectoryStore(MxisdConfig cfg) {
this(cfg.getExec().getDirectory(), cfg.getMatrix());
}
public ExecDirectoryStore(ExecConfig.Directory cfg, MatrixConfig mxCfg) {
@@ -47,11 +44,6 @@ public class ExecDirectoryStore extends ExecStore implements IDirectoryProvider
this.mxCfg = mxCfg;
}
@Override
public boolean isEnabled() {
return cfg.isEnabled();
}
private UserDirectorySearchResult search(ExecConfig.Process cfg, UserDirectorySearchRequest request) {
if (StringUtils.isEmpty(cfg.getCommand())) {
return UserDirectorySearchResult.empty();

View File

@@ -40,8 +40,6 @@ import io.kamax.mxisd.lookup.provider.IThreePidProvider;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Collections;
import java.util.List;
@@ -49,15 +47,13 @@ import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
@Component
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 MatrixConfig mxCfg;
@Autowired
public ExecIdentityStore(ExecConfig cfg, MatrixConfig mxCfg) {
this(cfg.getIdentity(), mxCfg);
}
@@ -67,11 +63,6 @@ public class ExecIdentityStore extends ExecStore implements IThreePidProvider {
this.mxCfg = mxCfg;
}
@Override
public boolean isEnabled() {
return cfg.isEnabled();
}
@Override
public boolean isLocal() {
return true;

View File

@@ -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()));
}
}
}

View File

@@ -28,19 +28,15 @@ import io.kamax.mxisd.profile.JsonProfileRequest;
import io.kamax.mxisd.profile.JsonProfileResult;
import io.kamax.mxisd.profile.ProfileProvider;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
@Component
public class ExecProfileStore extends ExecStore implements ProfileProvider {
private ExecConfig.Profile cfg;
@Autowired
public ExecProfileStore(ExecConfig cfg) {
this(cfg.getProfile());
}
@@ -49,11 +45,6 @@ public class ExecProfileStore extends ExecStore implements ProfileProvider {
this.cfg = cfg;
}
@Override
public boolean isEnabled() {
return cfg.isEnabled();
}
private Optional<JsonProfileResult> getFull(_MatrixID userId, ExecConfig.Process cfg) {
Processor<Optional<JsonProfileResult>> p = new Processor<>(cfg);

View File

@@ -47,7 +47,7 @@ public class ExecStore {
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);

View File

@@ -29,6 +29,7 @@ import io.kamax.matrix._MatrixID;
import io.kamax.mxisd.UserIdType;
import io.kamax.mxisd.auth.provider.AuthenticatorProvider;
import io.kamax.mxisd.auth.provider.BackendAuthResult;
import io.kamax.mxisd.config.FirebaseConfig;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -38,10 +39,14 @@ import java.util.concurrent.TimeUnit;
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();
public GoogleFirebaseAuthenticator(FirebaseConfig cfg) {
this(cfg.isEnabled(), cfg.getCredentials(), cfg.getDatabase());
}
public GoogleFirebaseAuthenticator(boolean isEnabled, String credsPath, String db) {
super(isEnabled, "AuthenticationProvider", credsPath, db);
}

View File

@@ -35,7 +35,7 @@ import java.io.IOException;
public class GoogleFirebaseBackend {
private Logger log = LoggerFactory.getLogger(GoogleFirebaseBackend.class);
private transient final Logger log = LoggerFactory.getLogger(GoogleFirebaseBackend.class);
private boolean isEnabled;
private FirebaseAuth fbAuth;
@@ -60,7 +60,9 @@ public class GoogleFirebaseBackend {
private FirebaseCredential getCreds(String credsPath) throws IOException {
if (StringUtils.isNotBlank(credsPath)) {
return FirebaseCredentials.fromCertificate(new FileInputStream(credsPath));
try (FileInputStream is = new FileInputStream(credsPath)) {
return FirebaseCredentials.fromCertificate(is);
}
} else {
return FirebaseCredentials.applicationDefault();
}

View File

@@ -25,6 +25,7 @@ import com.google.firebase.tasks.OnFailureListener;
import com.google.firebase.tasks.OnSuccessListener;
import io.kamax.matrix.MatrixID;
import io.kamax.matrix.ThreePidMedium;
import io.kamax.mxisd.config.MxisdConfig;
import io.kamax.mxisd.lookup.SingleLookupReply;
import io.kamax.mxisd.lookup.SingleLookupRequest;
import io.kamax.mxisd.lookup.ThreePidMapping;
@@ -40,9 +41,13 @@ import java.util.concurrent.TimeUnit;
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;
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) {
super(isEnabled, "ThreePidProvider", credsPath, db);
this.domain = domain;

View File

@@ -1,6 +1,6 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2017 Kamax Sarl
* Copyright (C) 2018 Kamax Sarl
*
* https://www.kamax.io/
*
@@ -18,21 +18,26 @@
* 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 org.springframework.boot.diagnostics.AbstractFailureAnalyzer;
import org.springframework.boot.diagnostics.FailureAnalysis;
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.lookup.ThreePidProviders;
public class ConfigurationFailureAnalyzer extends AbstractFailureAnalyzer<ConfigurationException> {
public class GoogleFirebaseStoreSupplier implements IdentityStoreSupplier {
@Override
protected FailureAnalysis analyze(Throwable rootFailure, ConfigurationException cause) {
String message = cause.getMessage();
if (cause.getDetailedMessage().isPresent()) {
message += " - " + cause.getDetailedMessage().get();
public void accept(Mxisd mxisd) {
accept(mxisd.getConfig());
}
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);
}
}

View File

@@ -30,6 +30,7 @@ import io.kamax.mxisd.auth.provider.AuthenticatorProvider;
import io.kamax.mxisd.auth.provider.BackendAuthResult;
import io.kamax.mxisd.config.MatrixConfig;
import io.kamax.mxisd.config.ldap.LdapConfig;
import io.kamax.mxisd.exception.InternalServerError;
import io.kamax.mxisd.util.GsonUtil;
import org.apache.commons.lang.StringUtils;
import org.apache.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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.util.HashSet;
@@ -51,14 +50,12 @@ import java.util.List;
import java.util.Optional;
import java.util.Set;
@Component
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();
@Autowired
public LdapAuthProvider(LdapConfig cfg, MatrixConfig mxCfg) {
super(cfg, mxCfg);
}
@@ -87,7 +84,6 @@ public class LdapAuthProvider extends LdapBackend implements AuthenticatorProvid
public BackendAuthResult authenticate(_MatrixID mxid, String password) {
log.info("Performing auth for {}", mxid);
try (LdapConnection conn = getConn()) {
bind(conn);
@@ -108,11 +104,13 @@ public class LdapAuthProvider extends LdapBackend implements AuthenticatorProvid
String[] attArray = new String[attributes.size()];
attributes.toArray(attArray);
log.debug("Base DN: {}", getBaseDn());
log.debug("Query: {}", userFilter);
log.debug("Attributes: {}", GsonUtil.build().toJson(attArray));
try (EntryCursor cursor = conn.search(getBaseDn(), userFilter, SearchScope.SUBTREE, attArray)) {
for (String baseDN : getBaseDNs()) {
log.debug("Base DN: {}", baseDN);
try (EntryCursor cursor = conn.search(baseDN, userFilter, SearchScope.SUBTREE, attArray)) {
while (cursor.next()) {
Entry entry = cursor.get();
String dn = entry.getDn().getName();
@@ -159,11 +157,12 @@ public class LdapAuthProvider extends LdapBackend implements AuthenticatorProvid
} catch (CursorLdapReferralException e) {
log.warn("Entity for {} is only available via referral, skipping", mxid);
}
}
log.info("No match were found for {}", mxid);
return BackendAuthResult.failure();
} catch (LdapException | IOException | CursorException e) {
throw new RuntimeException(e);
throw new InternalServerError(e);
}
}

View File

@@ -45,7 +45,7 @@ public abstract class LdapBackend {
public static final String UID = "uid";
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 MatrixConfig mxCfg;
@@ -59,8 +59,8 @@ public abstract class LdapBackend {
return cfg;
}
protected String getBaseDn() {
return cfg.getConnection().getBaseDn();
protected List<String> getBaseDNs() {
return cfg.getConnection().getBaseDNs();
}
protected LdapConfig.Attribute getAt() {

View File

@@ -22,9 +22,9 @@ package io.kamax.mxisd.backend.ldap;
import io.kamax.mxisd.config.MatrixConfig;
import io.kamax.mxisd.config.ldap.LdapConfig;
import io.kamax.mxisd.controller.directory.v1.io.UserDirectorySearchResult;
import io.kamax.mxisd.directory.IDirectoryProvider;
import io.kamax.mxisd.directory.DirectoryProvider;
import io.kamax.mxisd.exception.InternalServerError;
import io.kamax.mxisd.http.io.UserDirectorySearchResult;
import io.kamax.mxisd.util.GsonUtil;
import org.apache.directory.api.ldap.model.cursor.CursorException;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@Component
public class LdapDirectoryProvider extends LdapBackend implements IDirectoryProvider {
public class LdapDirectoryProvider extends LdapBackend implements DirectoryProvider {
private Logger log = LoggerFactory.getLogger(LdapDirectoryProvider.class);
private transient final Logger log = LoggerFactory.getLogger(LdapDirectoryProvider.class);
@Autowired
public LdapDirectoryProvider(LdapConfig cfg, MatrixConfig mxCfg) {
super(cfg, mxCfg);
}
@Override
public boolean isEnabled() {
return getCfg().isEnabled();
}
protected UserDirectorySearchResult search(String query, List<String> attributes) {
UserDirectorySearchResult result = new UserDirectorySearchResult();
result.setLimited(false);
@@ -65,18 +56,19 @@ public class LdapDirectoryProvider extends LdapBackend implements IDirectoryProv
bind(conn);
LdapConfig.Attribute atCfg = getCfg().getAttribute();
attributes = new ArrayList<>(attributes);
attributes.add(getUidAtt());
String[] attArray = new String[attributes.size()];
attributes.toArray(attArray);
String searchQuery = buildOrQueryWithFilter(getCfg().getDirectory().getFilter(), "*" + query + "*", attArray);
log.debug("Base DN: {}", getBaseDn());
log.debug("Query: {}", searchQuery);
log.debug("Attributes: {}", GsonUtil.build().toJson(attArray));
try (EntryCursor cursor = conn.search(getBaseDn(), searchQuery, SearchScope.SUBTREE, attArray)) {
for (String baseDN : getBaseDNs()) {
log.debug("Base DN: {}", baseDN);
try (EntryCursor cursor = conn.search(baseDN, searchQuery, SearchScope.SUBTREE, attArray)) {
while (cursor.next()) {
Entry entry = cursor.get();
log.info("Found possible match, DN: {}", entry.getDn().getName());
@@ -93,6 +85,7 @@ public class LdapDirectoryProvider extends LdapBackend implements IDirectoryProv
});
}
}
}
} catch (CursorLdapReferralException e) {
log.warn("An entry is only available via referral, skipping");
} catch (IOException | LdapException | CursorException e) {

View File

@@ -36,8 +36,6 @@ import org.apache.directory.api.ldap.model.message.SearchScope;
import org.apache.directory.ldap.client.api.LdapConnection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.util.ArrayList;
@@ -45,21 +43,14 @@ import java.util.Collections;
import java.util.List;
import java.util.Optional;
@Component
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) {
super(cfg, mxCfg);
}
@Override
public boolean isEnabled() {
return getCfg().isEnabled();
}
@Override
public Optional<String> getDisplayName(_MatrixID userId) {
String uid = buildUidFromMatrixId(userId);
@@ -69,11 +60,11 @@ public class LdapProfileProvider extends LdapBackend implements ProfileProvider
bind(conn);
String searchQuery = buildOrQueryWithFilter(getCfg().getProfile().getFilter(), uid, getUidAtt());
log.debug("Base DN: {}", getBaseDn());
log.debug("Query: {}", searchQuery);
try (EntryCursor cursor = conn.search(getBaseDn(), searchQuery, SearchScope.SUBTREE, getAt().getName())) {
for (String baseDN : getBaseDNs()) {
log.debug("Base DN: {}", baseDN);
try (EntryCursor cursor = conn.search(baseDN, searchQuery, SearchScope.SUBTREE, getAt().getName())) {
while (cursor.next()) {
Entry entry = cursor.get();
log.info("Found possible match, DN: {}", entry.getDn().getName());
@@ -92,9 +83,10 @@ public class LdapProfileProvider extends LdapBackend implements ProfileProvider
return v;
}
}
}
} catch (CursorLdapReferralException e) {
log.warn("An entry is only available via referral, skipping");
}
}
} catch (IOException | LdapException | CursorException e) {
throw new InternalServerError(e);
}
@@ -111,7 +103,6 @@ public class LdapProfileProvider extends LdapBackend implements ProfileProvider
try (LdapConnection conn = getConn()) {
bind(conn);
log.debug("Base DN: {}", getBaseDn());
getCfg().getAttribute().getThreepid().forEach((medium, attributes) -> {
String[] attArray = new String[attributes.size()];
attributes.toArray(attArray);
@@ -120,7 +111,9 @@ public class LdapProfileProvider extends LdapBackend implements ProfileProvider
log.debug("Query for 3PID {}: {}", medium, searchQuery);
try (EntryCursor cursor = conn.search(getBaseDn(), searchQuery, SearchScope.SUBTREE, attArray)) {
for (String baseDN : getBaseDNs()) {
log.debug("Base DN: {}", baseDN);
try (EntryCursor cursor = conn.search(baseDN, searchQuery, SearchScope.SUBTREE, attArray)) {
while (cursor.next()) {
Entry entry = cursor.get();
log.info("Found possible match, DN: {}", entry.getDn().getName());
@@ -137,11 +130,11 @@ public class LdapProfileProvider extends LdapBackend implements ProfileProvider
}
} catch (CursorLdapReferralException e) {
log.warn("An entry is only available via referral, skipping");
} catch (IOException | LdapException | CursorException e) {
} catch (LdapException | IOException | CursorException e) {
throw new InternalServerError(e);
}
}
});
} catch (IOException | LdapException e) {
throw new InternalServerError(e);
}

View File

@@ -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()));
}
}
}

View File

@@ -37,27 +37,20 @@ import org.apache.directory.api.ldap.model.message.SearchScope;
import org.apache.directory.ldap.client.api.LdapConnection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@Component
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) {
super(cfg, mxCfg);
}
@Override
public boolean isEnabled() {
return getCfg().isEnabled();
}
@Override
public boolean isLocal() {
return true;
@@ -78,12 +71,13 @@ public class LdapThreePidProvider extends LdapBackend implements IThreePidProvid
// we merge 3PID specific query with global/specific filter, if one exists.
String tPidQuery = tPidQueryOpt.get().replaceAll(getCfg().getIdentity().getToken(), value);
String searchQuery = buildWithFilter(tPidQuery, getCfg().getIdentity().getFilter());
log.debug("Base DN: {}", getBaseDn());
log.debug("Query: {}", searchQuery);
log.debug("Attributes: {}", GsonUtil.build().toJson(getUidAtt()));
try (EntryCursor cursor = conn.search(getBaseDn(), searchQuery, SearchScope.SUBTREE, getUidAtt())) {
for (String baseDN : getBaseDNs()) {
log.debug("Base DN: {}", baseDN);
try (EntryCursor cursor = conn.search(baseDN, searchQuery, SearchScope.SUBTREE, getUidAtt())) {
while (cursor.next()) {
Entry entry = cursor.get();
log.info("Found possible match, DN: {}", entry.getDn().getName());
@@ -101,6 +95,7 @@ public class LdapThreePidProvider extends LdapBackend implements IThreePidProvid
} catch (IOException | LdapException | CursorException e) {
throw new InternalServerError(e);
}
}
return Optional.empty();
}

View File

@@ -23,9 +23,7 @@ package io.kamax.mxisd.backend.ldap.netiq;
import io.kamax.mxisd.backend.ldap.LdapAuthProvider;
import io.kamax.mxisd.config.MatrixConfig;
import io.kamax.mxisd.config.ldap.netiq.NetIqLdapConfig;
import org.springframework.stereotype.Component;
@Component
public class NetIqLdapAuthProvider extends LdapAuthProvider {
public NetIqLdapAuthProvider(NetIqLdapConfig cfg, MatrixConfig mxCfg) {

View File

@@ -23,9 +23,7 @@ package io.kamax.mxisd.backend.ldap.netiq;
import io.kamax.mxisd.backend.ldap.LdapDirectoryProvider;
import io.kamax.mxisd.config.MatrixConfig;
import io.kamax.mxisd.config.ldap.netiq.NetIqLdapConfig;
import org.springframework.stereotype.Component;
@Component
public class NetIqLdapDirectoryProvider extends LdapDirectoryProvider {
public NetIqLdapDirectoryProvider(NetIqLdapConfig cfg, MatrixConfig mxCfg) {

View File

@@ -24,13 +24,9 @@ import io.kamax.matrix._MatrixID;
import io.kamax.mxisd.backend.ldap.LdapProfileProvider;
import io.kamax.mxisd.config.MatrixConfig;
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 {
@Autowired
public NetIqLdapProfileProvider(NetIqLdapConfig cfg, MatrixConfig mxCfg) {
super(cfg, mxCfg);
}

View File

@@ -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()));
}
}
}

View File

@@ -23,9 +23,7 @@ package io.kamax.mxisd.backend.ldap.netiq;
import io.kamax.mxisd.backend.ldap.LdapThreePidProvider;
import io.kamax.mxisd.config.MatrixConfig;
import io.kamax.mxisd.config.ldap.netiq.NetIqLdapConfig;
import org.springframework.stereotype.Component;
@Component
public class NetIqLdapThreePidProvider extends LdapThreePidProvider {
public NetIqLdapThreePidProvider(NetIqLdapConfig cfg, MatrixConfig mxCfg) {

View File

@@ -31,8 +31,8 @@ import io.kamax.mxisd.config.MatrixConfig;
import io.kamax.mxisd.config.memory.MemoryIdentityConfig;
import io.kamax.mxisd.config.memory.MemoryStoreConfig;
import io.kamax.mxisd.config.memory.MemoryThreePid;
import io.kamax.mxisd.controller.directory.v1.io.UserDirectorySearchResult;
import io.kamax.mxisd.directory.IDirectoryProvider;
import io.kamax.mxisd.directory.DirectoryProvider;
import io.kamax.mxisd.http.io.UserDirectorySearchResult;
import io.kamax.mxisd.lookup.SingleLookupReply;
import io.kamax.mxisd.lookup.SingleLookupRequest;
import io.kamax.mxisd.lookup.ThreePidMapping;
@@ -41,8 +41,6 @@ import io.kamax.mxisd.profile.ProfileProvider;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.Collections;
@@ -51,15 +49,13 @@ import java.util.Optional;
import java.util.function.Function;
import java.util.function.Predicate;
@Component
public class MemoryIdentityStore implements AuthenticatorProvider, IDirectoryProvider, IThreePidProvider, ProfileProvider {
public class MemoryIdentityStore implements AuthenticatorProvider, DirectoryProvider, 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 MemoryStoreConfig cfg;
@Autowired
public MemoryIdentityStore(MatrixConfig mxCfg, MemoryStoreConfig cfg) {
this.mxCfg = mxCfg;
this.cfg = cfg;

View File

@@ -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);
}
}
}

View File

@@ -37,4 +37,5 @@ public class LookupSingleRequestJson {
public String getAddress() {
return address;
}
}

View File

@@ -27,15 +27,11 @@ import io.kamax.mxisd.config.rest.RestBackendConfig;
import io.kamax.mxisd.util.RestClientUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpUriRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.IOException;
@Component
public class RestAuthProvider extends RestProvider implements AuthenticatorProvider {
@Autowired
public RestAuthProvider(RestBackendConfig cfg) {
super(cfg);
}

View File

@@ -23,21 +23,18 @@ package io.kamax.mxisd.backend.rest;
import io.kamax.matrix.MatrixID;
import io.kamax.mxisd.config.MatrixConfig;
import io.kamax.mxisd.config.rest.RestBackendConfig;
import io.kamax.mxisd.controller.directory.v1.io.UserDirectorySearchRequest;
import io.kamax.mxisd.controller.directory.v1.io.UserDirectorySearchResult;
import io.kamax.mxisd.directory.IDirectoryProvider;
import io.kamax.mxisd.directory.DirectoryProvider;
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 org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
@Component
public class RestDirectoryProvider extends RestProvider implements IDirectoryProvider {
public class RestDirectoryProvider extends RestProvider implements DirectoryProvider {
private MatrixConfig mxCfg;
@@ -46,11 +43,6 @@ public class RestDirectoryProvider extends RestProvider implements IDirectoryPro
this.mxCfg = mxCfg;
}
@Override
public boolean isEnabled() {
return cfg.isEnabled() && StringUtils.isNotBlank(cfg.getEndpoints().getDirectory());
}
private UserDirectorySearchResult search(String by, String query) {
UserDirectorySearchRequest request = new UserDirectorySearchRequest(query);
request.setBy(by);

View File

@@ -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);
}
}

View File

@@ -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()));
}
}
}

View File

@@ -35,8 +35,6 @@ import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpUriRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.util.ArrayList;
@@ -44,14 +42,12 @@ import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
@Component
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
@Autowired
public RestThreePidProvider(RestBackendConfig cfg, MatrixConfig mxCfg) {
super(cfg);
this.mxCfg = mxCfg;
@@ -66,11 +62,6 @@ public class RestThreePidProvider extends RestProvider implements IThreePidProvi
}
}
@Override
public boolean isEnabled() {
return cfg.isEnabled();
}
@Override
public boolean isLocal() {
return true;

View File

@@ -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);
}
}
}
}

View 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> {
}

View File

@@ -1,6 +1,6 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2017 Kamax Sarl
* Copyright (C) 2019 Kamax Sàrl
*
* https://www.kamax.io/
*
@@ -18,16 +18,21 @@
* 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 org.springframework.boot.autoconfigure.SpringBootApplication;
import java.util.Objects;
import java.util.ServiceLoader;
@SpringBootApplication
public class MatrixIdentityServerApplication {
public class Drivers {
public static void main(String[] args) {
SpringApplication.run(MatrixIdentityServerApplication.class, args);
private static ServiceLoader<DriverLoader> svcLoader;
public static void load(String type) {
if (Objects.isNull(svcLoader)) {
svcLoader = ServiceLoader.load(DriverLoader.class);
}
svcLoader.iterator().forEachRemaining(drv -> drv.accept(type));
}
}

View File

@@ -37,11 +37,15 @@ public class SqlConnectionPool {
private ComboPooledDataSource ds;
public SqlConnectionPool(SqlConfig cfg) {
Drivers.load(cfg.getType());
ds = new ComboPooledDataSource();
ds.setJdbcUrl("jdbc:" + cfg.getType() + ":" + cfg.getConnection());
ds.setMinPoolSize(1);
ds.setMaxPoolSize(10);
ds.setAcquireIncrement(2);
ds.setAcquireRetryAttempts(10);
ds.setAcquireRetryDelay(1000);
}
public Connection get() throws SQLException {

View File

@@ -39,7 +39,7 @@ import java.util.Optional;
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;
@@ -50,11 +50,6 @@ public abstract class SqlProfileProvider implements ProfileProvider {
this.pool = new SqlConnectionPool(cfg);
}
@Override
public boolean isEnabled() {
return cfg.isEnabled();
}
@Override
public Optional<String> getDisplayName(_MatrixID user) {
String stmtSql = cfg.getDisplayName().getQuery();

View File

@@ -41,7 +41,7 @@ import java.util.Optional;
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 MatrixConfig mxCfg;
@@ -54,11 +54,6 @@ public abstract class SqlThreePidProvider implements IThreePidProvider {
this.mxCfg = mxCfg;
}
@Override
public boolean isEnabled() {
return cfg.isEnabled();
}
@Override
public boolean isLocal() {
return true;

View File

@@ -27,20 +27,19 @@ import io.kamax.mxisd.config.sql.generic.GenericSqlProviderConfig;
import io.kamax.mxisd.invitation.InvitationManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
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;
@Autowired
private InvitationManager invMgr;
public GenericSqlAuthProvider(GenericSqlProviderConfig cfg, InvitationManager invMgr) {
this.cfg = cfg;
this.invMgr = invMgr;
}
@Override
public boolean isEnabled() {
return cfg.getAuth().isEnabled();

View File

@@ -25,9 +25,9 @@ import io.kamax.mxisd.backend.sql.SqlConnectionPool;
import io.kamax.mxisd.config.MatrixConfig;
import io.kamax.mxisd.config.sql.SqlConfig;
import io.kamax.mxisd.config.sql.generic.GenericSqlProviderConfig;
import io.kamax.mxisd.controller.directory.v1.io.UserDirectorySearchResult;
import io.kamax.mxisd.directory.IDirectoryProvider;
import io.kamax.mxisd.directory.DirectoryProvider;
import io.kamax.mxisd.exception.InternalServerError;
import io.kamax.mxisd.http.io.UserDirectorySearchResult;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -38,11 +38,11 @@ import java.sql.ResultSet;
import java.sql.SQLException;
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 MatrixConfig mxCfg;
@@ -55,11 +55,6 @@ public abstract class GenericSqlDirectoryProvider implements IDirectoryProvider
this.mxCfg = mxCfg;
}
@Override
public boolean isEnabled() {
return cfg.getDirectory().isEnabled();
}
protected void setParameters(PreparedStatement stmt, String searchTerm) throws SQLException {
for (int i = 1; i <= stmt.getParameterMetaData().getParameterCount(); i++) {
stmt.setString(i, searchTerm);

View File

@@ -22,9 +22,7 @@ package io.kamax.mxisd.backend.sql.generic;
import io.kamax.mxisd.backend.sql.SqlProfileProvider;
import io.kamax.mxisd.config.sql.generic.GenericSqlProviderConfig;
import org.springframework.stereotype.Component;
@Component
public class GenericSqlProfileProvider extends SqlProfileProvider {
public GenericSqlProfileProvider(GenericSqlProviderConfig cfg) {

View File

@@ -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()));
}
}
}

View File

@@ -23,13 +23,9 @@ package io.kamax.mxisd.backend.sql.generic;
import io.kamax.mxisd.backend.sql.SqlThreePidProvider;
import io.kamax.mxisd.config.MatrixConfig;
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 {
@Autowired
public GenericSqlThreePidProvider(GenericSqlProviderConfig cfg, MatrixConfig mxCfg) {
super(cfg, mxCfg);
}

View File

@@ -22,19 +22,15 @@ package io.kamax.mxisd.backend.sql.synapse;
import io.kamax.mxisd.backend.sql.SqlConnectionPool;
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.ResultSet;
import java.util.Optional;
@Component
public class Synapse {
private SqlConnectionPool pool;
@Autowired
public Synapse(SynapseSqlProviderConfig sqlCfg) {
this.pool = new SqlConnectionPool(sqlCfg);
}

View File

@@ -24,32 +24,15 @@ import io.kamax.mxisd.backend.sql.generic.GenericSqlDirectoryProvider;
import io.kamax.mxisd.config.MatrixConfig;
import io.kamax.mxisd.config.sql.generic.GenericSqlProviderConfig;
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.SQLException;
import java.util.Objects;
@Component
public class SynapseSqlDirectoryProvider extends GenericSqlDirectoryProvider {
@Autowired
public SynapseSqlDirectoryProvider(SynapseSqlProviderConfig cfg, MatrixConfig 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();
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 + "%");
}
}

View File

@@ -22,13 +22,9 @@ package io.kamax.mxisd.backend.sql.synapse;
import io.kamax.mxisd.backend.sql.SqlProfileProvider;
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 {
@Autowired
public SynapseSqlProfileProvider(SynapseSqlProviderConfig cfg) {
super(cfg);
}

View File

@@ -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()));
}
}
}

View File

@@ -23,13 +23,9 @@ package io.kamax.mxisd.backend.sql.synapse;
import io.kamax.mxisd.backend.sql.SqlThreePidProvider;
import io.kamax.mxisd.config.MatrixConfig;
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 {
@Autowired
public SynapseSqlThreePidProvider(SynapseSqlProviderConfig cfg, MatrixConfig mxCfg) {
super(cfg, mxCfg);
}

View File

@@ -28,17 +28,13 @@ import io.kamax.mxisd.auth.provider.BackendAuthResult;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
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;
@Autowired
public WordpressAuthProvider(WordpressRestBackend wordpress) {
this.wordpress = wordpress;
}

View File

@@ -23,13 +23,11 @@ package io.kamax.mxisd.backend.wordpress;
import io.kamax.matrix.MatrixID;
import io.kamax.mxisd.config.MatrixConfig;
import io.kamax.mxisd.config.wordpress.WordpressConfig;
import io.kamax.mxisd.controller.directory.v1.io.UserDirectorySearchResult;
import io.kamax.mxisd.directory.IDirectoryProvider;
import io.kamax.mxisd.directory.DirectoryProvider;
import io.kamax.mxisd.exception.InternalServerError;
import io.kamax.mxisd.http.io.UserDirectorySearchResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.sql.Connection;
import java.sql.PreparedStatement;
@@ -37,27 +35,20 @@ import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Optional;
@Component
public class WordpressDirectoryProvider implements IDirectoryProvider {
public class WordpressDirectoryProvider implements DirectoryProvider {
private final Logger log = LoggerFactory.getLogger(WordpressDirectoryProvider.class);
private transient final Logger log = LoggerFactory.getLogger(WordpressDirectoryProvider.class);
private WordpressConfig cfg;
private WordressSqlBackend wordpress;
private MatrixConfig mxCfg;
@Autowired
public WordpressDirectoryProvider(WordpressConfig cfg, WordressSqlBackend wordpress, MatrixConfig mxCfg) {
this.cfg = cfg;
this.wordpress = wordpress;
this.mxCfg = mxCfg;
}
@Override
public boolean isEnabled() {
return wordpress.isEnabled();
}
protected void setParameters(PreparedStatement stmt, String searchTerm) throws SQLException {
for (int i = 1; i <= stmt.getParameterMetaData().getParameterCount(); i++) {
stmt.setString(i, "%" + searchTerm + "%");

View File

@@ -34,15 +34,12 @@ import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.IOException;
@Component
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 jwtPath = "/jwt-auth/v1";
@@ -54,7 +51,6 @@ public class WordpressRestBackend {
private String token;
@Autowired
public WordpressRestBackend(WordpressConfig cfg, CloseableHttpClient client) {
this.cfg = cfg;
this.client = client;

View File

@@ -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));
}
}
}

View File

@@ -31,8 +31,6 @@ import io.kamax.mxisd.lookup.ThreePidMapping;
import io.kamax.mxisd.lookup.provider.IThreePidProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.sql.Connection;
import java.sql.PreparedStatement;
@@ -42,27 +40,20 @@ import java.util.List;
import java.util.Objects;
import java.util.Optional;
@Component
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 WordpressConfig cfg;
private WordressSqlBackend wordpress;
@Autowired
public WordpressThreePidProvider(MatrixConfig mxCfg, WordpressConfig cfg, WordressSqlBackend wordpress) {
this.mxCfg = mxCfg;
this.cfg = cfg;
this.wordpress = wordpress;
}
@Override
public boolean isEnabled() {
return wordpress.isEnabled();
}
@Override
public boolean isLocal() {
return true;

View File

@@ -24,22 +24,17 @@ import com.mchange.v2.c3p0.ComboPooledDataSource;
import io.kamax.mxisd.config.wordpress.WordpressConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.sql.Connection;
import java.sql.SQLException;
@Component
public class WordressSqlBackend {
private Logger log = LoggerFactory.getLogger(WordressSqlBackend.class);
private transient final Logger log = LoggerFactory.getLogger(WordressSqlBackend.class);
private WordpressConfig cfg;
private ComboPooledDataSource ds;
@Autowired
public WordressSqlBackend(WordpressConfig cfg) {
this.cfg = cfg;

View 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())));
}
}

View File

@@ -20,14 +20,8 @@
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;
@Configuration
@ConfigurationProperties(prefix = "lookup.bulk")
public class BulkLookupConfig {
private Boolean enabled;
@@ -40,7 +34,6 @@ public class BulkLookupConfig {
this.enabled = enabled;
}
@PostConstruct
public void build() {
if (Objects.isNull(enabled)) {
enabled = true;

View File

@@ -22,16 +22,10 @@ package io.kamax.mxisd.config;
import org.slf4j.Logger;
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 {
private final transient Logger log = LoggerFactory.getLogger(DnsOverwriteConfig.class);
private final static Logger log = LoggerFactory.getLogger(DirectoryConfig.class);
public static class Exclude {
@@ -67,12 +61,11 @@ public class DirectoryConfig {
this.exclude = exclude;
}
@PostConstruct
public void buid() {
public void build() {
log.info("--- Directory config ---");
log.info("Exclude:");
log.info("\tHomeserver: {}", getExclude().getHomeserver());
log.info("\t3PID: {}", getExclude().getThreepid());
log.info(" Homeserver: {}", getExclude().getHomeserver());
log.info(" 3PID: {}", getExclude().getThreepid());
}
}

View File

@@ -24,18 +24,13 @@ import com.google.gson.Gson;
import io.kamax.mxisd.util.GsonUtil;
import org.slf4j.Logger;
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.List;
@Configuration
@ConfigurationProperties("dns.overwrite")
public class DnsOverwriteConfig {
private Logger log = LoggerFactory.getLogger(DnsOverwriteConfig.class);
private transient final Logger log = LoggerFactory.getLogger(DnsOverwriteConfig.class);
public static class Entry {
@@ -93,7 +88,6 @@ public class DnsOverwriteConfig {
this.homeserver = homeserver;
}
@PostConstruct
public void build() {
Gson gson = GsonUtil.build();
log.info("--- DNS Overwrite config ---");

View File

@@ -21,14 +21,9 @@
package io.kamax.mxisd.config;
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.*;
@Configuration
@ConfigurationProperties("exec")
public class ExecConfig {
public class IO {
@@ -516,8 +511,7 @@ public class ExecConfig {
this.profile = profile;
}
@PostConstruct
public ExecConfig compute() {
public ExecConfig build() {
if (Objects.isNull(getAuth().isEnabled())) {
getAuth().setEnabled(isEnabled());
}

View File

@@ -20,27 +20,12 @@
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.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 {
private Logger log = LoggerFactory.getLogger(FirebaseConfig.class);
@Autowired
private MatrixConfig mxCfg;
private transient final Logger log = LoggerFactory.getLogger(FirebaseConfig.class);
private boolean enabled;
private String credentials;
@@ -70,7 +55,6 @@ public class FirebaseConfig {
this.database = database;
}
@PostConstruct
public void build() {
log.info("--- Firebase configuration ---");
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());
}
}

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