Compare commits

..

6 Commits

Author SHA1 Message Date
Maxime Dor
3d39ee97dd Add 3PIDs at registration 2018-04-13 03:48:32 +02:00
Maxime Dor
94d60b6fc9 Auto-generate username 2018-04-13 03:48:32 +02:00
Maxime Dor
ac9881fa4b Fix login 2018-04-13 03:48:32 +02:00
Maxime Dor
547cafdd13 PoC of registration with Google 3PID 2018-04-13 03:48:32 +02:00
Maxime Dor
0da1af9869 registration handling skeleton 2018-04-13 03:48:32 +02:00
Maxime Dor
268df533f5 Research with Google auth integration 2018-04-13 03:48:32 +02:00
594 changed files with 6174 additions and 30930 deletions

3
.gitignore vendored
View File

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

View File

@@ -2,17 +2,16 @@ FROM openjdk:8-jre-alpine
RUN apk update && apk add bash && rm -rf /var/lib/apk/* /var/cache/apk/* RUN apk update && apk add bash && rm -rf /var/lib/apk/* /var/cache/apk/*
VOLUME /etc/ma1sd VOLUME /etc/mxisd
VOLUME /var/ma1sd VOLUME /var/mxisd
EXPOSE 8090 EXPOSE 8090
ENV JAVA_OPTS="" ENV JAVA_OPTS=""
ENV CONF_FILE_PATH="/etc/ma1sd/ma1sd.yaml" ENV CONF_FILE_PATH="/etc/mxisd/mxisd.yaml"
ENV SIGN_KEY_PATH="/var/ma1sd/sign.key" ENV SIGN_KEY_PATH="/var/mxisd/sign.key"
ENV SQLITE_DATABASE_PATH="/var/ma1sd/ma1sd.db" ENV SQLITE_DATABASE_PATH="/var/mxisd/mxisd.db"
CMD [ "/start.sh" ] CMD [ "/start.sh" ]
ADD src/docker/start.sh /start.sh ADD src/docker/start.sh /start.sh
ADD src/script/ma1sd /app/ma1sd ADD build/libs/mxisd.jar /mxisd.jar
ADD build/libs/ma1sd.jar /app/ma1sd.jar

View File

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

99
application.example.yaml Normal file
View File

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

View File

@@ -1,8 +1,8 @@
/* /*
* ma1sd - Matrix Identity Server Daemon * mxisd - Matrix Identity Server Daemon
* Copyright (C) 2017 Kamax Sarl * Copyright (C) 2017 Maxime Dor
* *
* https://www.kamax.io/ * https://max.kamax.io/
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as * it under the terms of the GNU Affero General Public License as
@@ -21,21 +21,17 @@
import java.util.regex.Pattern import java.util.regex.Pattern
apply plugin: 'java' apply plugin: 'java'
apply plugin: 'application' apply plugin: 'org.springframework.boot'
apply plugin: 'com.github.johnrengelman.shadow'
apply plugin: 'idea'
apply plugin: 'com.github.ben-manes.versions'
def confFileName = "ma1sd.example.yaml" def confFileName = "application.example.yaml"
def distDir = "${project.buildDir}/dist" def distDir = "${project.buildDir}/dist"
def debBinPath = "/usr/lib/ma1sd" def debBinPath = "/usr/lib/mxisd"
def debConfPath = "/etc/ma1sd" def debConfPath = "/etc/mxisd"
def debDataPath = "/var/lib/ma1sd" def debDataPath = "/var/lib/mxisd"
def debSystemdPath = "/etc/systemd/system" def debSystemdPath = "/etc/systemd/system"
def debConfFileName = confFileName def debConfFileName = "mxisd-sample.yaml"
def debStartScriptFilename = "ma1sd"
def debBuildBasePath = "${project.buildDir}/tmp/debian" def debBuildBasePath = "${project.buildDir}/tmp/debian"
def debBuildDebianPath = "${debBuildBasePath}/DEBIAN" def debBuildDebianPath = "${debBuildBasePath}/DEBIAN"
@@ -44,156 +40,139 @@ def debBuildConfPath = "${debBuildBasePath}${debConfPath}"
def debBuildDataPath = "${debBuildBasePath}${debDataPath}" def debBuildDataPath = "${debBuildBasePath}${debDataPath}"
def debBuildSystemdPath = "${debBuildBasePath}${debSystemdPath}" def debBuildSystemdPath = "${debBuildBasePath}${debSystemdPath}"
def dockerImageName = "ma1uta/ma1sd" def dockerImageName = "kamax/mxisd"
def dockerImageTag = "${dockerImageName}:${ma1sdVersion()}" def dockerImageTag = "${dockerImageName}:${gitVersion()}"
group = 'io.kamax'
mainClassName = 'io.kamax.mxisd.MxisdStandaloneExec'
sourceCompatibility = '1.8'
targetCompatibility = '1.8'
String ma1sdVersion() {
def versionPattern = Pattern.compile("v(\\d+\\.)?(\\d+\\.)?(\\d+)(-.*)?")
String version = System.getenv('MA1SD_BUILD_VERSION')
if (version == null || version.size() == 0) {
version = gitVersion()
}
return versionPattern.matcher(version).matches() ? version.substring(1) : version
}
String gitVersion() { String gitVersion() {
def versionPattern = Pattern.compile("v(\\d+\\.)?(\\d+\\.)?(\\d+)(-.*)?")
ByteArrayOutputStream out = new ByteArrayOutputStream() ByteArrayOutputStream out = new ByteArrayOutputStream()
exec { exec {
commandLine = ['git', 'describe', '--tags', '--always', '--dirty'] commandLine = ['git', 'describe', '--tags', '--always', '--dirty']
standardOutput = out standardOutput = out
} }
return out.toString().replace(System.lineSeparator(), '') def v = out.toString().replace(System.lineSeparator(), '')
return versionPattern.matcher(v).matches() ? v.substring(1) : v
} }
buildscript { buildscript {
repositories { repositories {
jcenter() mavenCentral()
} }
dependencies { dependencies {
classpath 'com.github.jengelman.gradle.plugins:shadow:5.1.0' classpath 'org.springframework.boot:spring-boot-gradle-plugin:1.5.3.RELEASE'
classpath 'com.github.ben-manes:gradle-versions-plugin:0.27.0'
} }
} }
repositories { repositories {
jcenter() maven { url "https://kamax.io/maven/releases/" }
maven { url "https://kamax.io/maven/snapshots/" }
mavenCentral()
} }
dependencies { dependencies {
// Logging
compile 'org.slf4j:slf4j-simple:1.7.25'
// Easy file management // Easy file management
compile 'commons-io:commons-io:2.6' compile 'commons-io:commons-io:2.5'
// Config management // Spring Boot - standalone app
compile 'org.yaml:snakeyaml:1.25' compile 'org.springframework.boot:spring-boot-starter-web:1.5.10.RELEASE'
// Dependencies from old Matrix-java-sdk // Thymeleaf for HTML templates
compile 'org.apache.commons:commons-lang3:3.9' compile "org.springframework.boot:spring-boot-starter-thymeleaf:1.5.10.RELEASE"
compile 'com.squareup.okhttp3:okhttp:4.2.2'
compile 'commons-codec:commons-codec:1.13'
// ORMLite // Matrix Java SDK
compile 'com.j256.ormlite:ormlite-jdbc:5.1' compile 'io.kamax:matrix-java-sdk:0.0.11'
// ed25519 handling // ed25519 handling
compile 'net.i2p.crypto:eddsa:0.3.0' compile 'net.i2p.crypto:eddsa:0.1.0'
// LDAP connector // LDAP connector
compile 'org.apache.directory.api:api-all:1.0.0' compile 'org.apache.directory.api:api-all:1.0.0'
// DNS lookups // DNS lookups
compile 'dnsjava:dnsjava:2.1.9' compile 'dnsjava:dnsjava:2.1.8'
// HTTP connections // HTTP connections
compile 'org.apache.httpcomponents:httpclient:4.5.10' compile 'org.apache.httpcomponents:httpclient:4.5.3'
// Phone numbers validation // Phone numbers validation
compile 'com.googlecode.libphonenumber:libphonenumber:8.10.22' compile 'com.googlecode.libphonenumber:libphonenumber:8.7.1'
// E-mail sending // E-mail sending
compile 'javax.mail:javax.mail-api:1.6.2' compile 'com.sun.mail:javax.mail:1.5.6'
compile 'com.sun.mail:javax.mail:1.6.2' compile 'javax.mail:javax.mail-api:1.5.6'
// Google Client APIs
compile 'com.google.api-client:google-api-client:1.23.0'
// Google Firebase Authentication backend // Google Firebase Authentication backend
compile 'com.google.firebase:firebase-admin:5.3.0' compile 'com.google.firebase:firebase-admin:5.3.0'
// ORMLite
compile 'com.j256.ormlite:ormlite-jdbc:5.0'
// Connection Pool // Connection Pool
compile 'com.mchange:c3p0:0.9.5.4' compile 'com.mchange:c3p0:0.9.5.2'
// SQLite // SQLite
compile 'org.xerial:sqlite-jdbc:3.28.0' compile 'org.xerial:sqlite-jdbc:3.20.0'
// PostgreSQL // PostgreSQL
compile 'org.postgresql:postgresql:42.2.8' compile 'org.postgresql:postgresql:42.1.4'
// MariaDB/MySQL // MariaDB/MySQL
compile 'org.mariadb.jdbc:mariadb-java-client:2.5.1' compile 'org.mariadb.jdbc:mariadb-java-client:2.1.2'
// Twilio SDK for SMS // Twilio SDK for SMS
compile 'com.twilio.sdk:twilio:7.45.0' compile 'com.twilio.sdk:twilio:7.14.5'
// SendGrid SDK to send emails from GCE // SendGrid SDK to send emails from GCE
compile 'com.sendgrid:sendgrid-java:2.2.2' compile 'com.sendgrid:sendgrid-java:2.2.2'
// ZT-Exec for exec identity store testCompile 'junit:junit:4.12'
compile 'org.zeroturnaround:zt-exec:1.11' testCompile 'com.github.tomakehurst:wiremock:2.8.0'
// HTTP server
compile 'io.undertow:undertow-core:2.0.27.Final'
// Command parser for AS interface
implementation 'commons-cli:commons-cli:1.4'
testCompile 'junit:junit:4.13-rc-1'
testCompile 'com.github.tomakehurst:wiremock:2.25.1'
testCompile 'com.unboundid:unboundid-ldapsdk:4.0.12'
testCompile 'com.icegreen:greenmail:1.5.11'
} }
jar { springBoot {
manifest { executable = true
attributes(
'Implementation-Version': ma1sdVersion() embeddedLaunchScriptProperties = [
) confFolder: "/etc/default"
} ]
} }
shadowJar { processResources {
baseName = project.name
classifier = null
version = null
}
task debBuild(dependsOn: shadowJar) {
doLast { doLast {
String debVersion = ma1sdVersion() copy {
println "Version for package: ${debVersion}" from('build/resources/main/application.yaml') {
rename 'application.yaml', 'mxisd.yaml'
}
into 'build/resources/main'
}
}
}
task buildDeb(dependsOn: build) {
doLast {
def v = gitVersion()
println "Version for package: ${v}"
mkdir distDir mkdir distDir
mkdir debBuildBasePath mkdir debBuildBasePath
mkdir debBuildDebianPath mkdir "${debBuildBasePath}/DEBIAN"
mkdir debBuildBinPath mkdir debBuildBinPath
mkdir debBuildConfPath mkdir debBuildConfPath
mkdir debBuildDataPath mkdir debBuildDataPath
mkdir debBuildSystemdPath mkdir debBuildSystemdPath
copy { copy {
from "${project.buildDir}/libs/ma1sd.jar" from "${project.buildDir}/libs/mxisd.jar"
into debBuildBinPath into debBuildBinPath
} }
copy { ant.chmod(
from "${project.file("src/script/" + debStartScriptFilename)}" file: "${debBuildBinPath}/mxisd.jar",
into debBuildBinPath perm: 'a+x'
} )
copy { copy {
from(project.file(confFileName)) { from(project.file(confFileName)) {
@@ -202,16 +181,16 @@ task debBuild(dependsOn: shadowJar) {
into debBuildConfPath into debBuildConfPath
} }
ant.replaceregexp( // FIXME adapt to new config format ant.replaceregexp(
file: "${debBuildConfPath}/${debConfFileName}", file: "${debBuildConfPath}/${debConfFileName}",
match: "key:\\R path:(.*)", match: "key.path:(.*)",
replace: "key:\n path: '${debDataPath}/keys'" replace: "key.path: '${debDataPath}/signing.key'"
) )
ant.replaceregexp( // FIXME adapt to new config format ant.replaceregexp(
file: "${debBuildConfPath}/${debConfFileName}", file: "${debBuildConfPath}/${debConfFileName}",
match: "storage:\\R provider:\\R sqlite:\\R database:(.*)", match: "storage.provider.sqlite.database:(.*)",
replace: "storage:\n provider:\n sqlite:\n database: '${debDataPath}/store.db'" replace: "storage.provider.sqlite.database: '${debDataPath}/mxisd.db'"
) )
copy { copy {
@@ -222,7 +201,7 @@ task debBuild(dependsOn: shadowJar) {
ant.replace( ant.replace(
file: "${debBuildDebianPath}/control", file: "${debBuildDebianPath}/control",
token: 'Version: 0', token: 'Version: 0',
value: "Version: ${debVersion}" value: "Version: ${v}"
) )
ant.replace( ant.replace(
@@ -231,12 +210,6 @@ task debBuild(dependsOn: shadowJar) {
value: debDataPath value: debDataPath
) )
ant.replace(
file: "${debBuildDebianPath}/postinst",
token: '%DEB_CONF_FILE%',
value: "${debConfPath}/ma1sd.yaml"
)
ant.chmod( ant.chmod(
file: "${debBuildDebianPath}/postinst", file: "${debBuildDebianPath}/postinst",
perm: 'a+x' perm: 'a+x'
@@ -248,7 +221,7 @@ task debBuild(dependsOn: shadowJar) {
) )
copy { copy {
from "${project.file('src/systemd/ma1sd.service')}" from "${project.file('src/systemd/mxisd.service')}"
into debBuildSystemdPath into debBuildSystemdPath
} }
@@ -264,7 +237,7 @@ task debBuild(dependsOn: shadowJar) {
} }
} }
task dockerBuild(type: Exec, dependsOn: shadowJar) { task dockerBuild(type: Exec, dependsOn: build) {
commandLine 'docker', 'build', '-t', dockerImageTag, project.rootDir commandLine 'docker', 'build', '-t', dockerImageTag, project.rootDir
doLast { doLast {

View File

@@ -1,130 +0,0 @@
# MSC2140
## V1 vs V2
In the [MSC2140](https://github.com/matrix-org/matrix-doc/pull/2140) the v2 prefix was introduced.
Default values:
```.yaml
matrix:
v1: true # deprecated
v2: true
```
To disable change value to `false`.
NOTE: the v1 is deprecated, therefore recommend to use only v2 and disable v1 (default value can be ommited):
```.yaml
matrix:
v1: false
```
NOTE: Riot Web version 1.5.5 and below checks the v1 for backward compatibility.
## Terms
Example:
```.yaml
policy:
policies:
term_name: # term name
version: 1.0 # version
terms:
en: # lang
name: term name en # localized name
url: https://ma1sd.host.tld/term_en.html # localized url
fe: # lang
name: term name fr # localized name
url: https://ma1sd.host.tld/term_fr.html # localized url
regexp:
- '/_matrix/identity/v2/account.*'
- '/_matrix/identity/v2/hash_details'
- '/_matrix/identity/v2/lookup'
```
Where:
- `term_name` -- name of the terms.
- `version` -- the terms version.
- `lang` -- the term language.
- `name` -- the name of the term.
- `url` -- the url of the term.
- `regexp` -- regexp patterns for API which should be available only after accepting the terms.
API will be checks for accepted terms only with authorization.
There are the next API:
- [`GET /_matrix/identity/v2/account`](https://matrix.org/docs/spec/identity_service/r0.3.0#get-matrix-identity-v2-account) - Gets information about what user owns the access token used in the request.
- [`POST /_matrix/identity/v2/account/logout`](https://matrix.org/docs/spec/identity_service/r0.3.0#post-matrix-identity-v2-account-logout) - Logs out the access token, preventing it from being used to authenticate future requests to the server.
- [`GET /_matrix/identity/v2/hash_details`](https://matrix.org/docs/spec/identity_service/r0.3.0#get-matrix-identity-v2-hash-details) - Gets parameters for hashing identifiers from the server. This can include any of the algorithms defined in this specification.
- [`POST /_matrix/identity/v2/lookup`](https://matrix.org/docs/spec/identity_service/r0.3.0#post-matrix-identity-v2-lookup) - Looks up the set of Matrix User IDs which have bound the 3PIDs given, if bindings are available. Note that the format of the addresses is defined later in this specification.
- [`POST /_matrix/identity/v2/validate/email/requestToken`](https://matrix.org/docs/spec/identity_service/r0.3.0#post-matrix-identity-v2-validate-email-requesttoken) - Create a session for validating an email address.
- [`POST /_matrix/identity/v2/validate/email/submitToken`](https://matrix.org/docs/spec/identity_service/r0.3.0#post-matrix-identity-v2-validate-email-submittoken) - Validate ownership of an email address.
- [`GET /_matrix/identity/v2/validate/email/submitToken`](https://matrix.org/docs/spec/identity_service/r0.3.0#get-matrix-identity-v2-validate-email-submittoken) - Validate ownership of an email address.
- [`POST /_matrix/identity/v2/validate/msisdn/requestToken`](https://matrix.org/docs/spec/identity_service/r0.3.0#post-matrix-identity-v2-validate-msisdn-requesttoken) - Create a session for validating a phone number.
- [`POST /_matrix/identity/v2/validate/msisdn/submitToken`](https://matrix.org/docs/spec/identity_service/r0.3.0#post-matrix-identity-v2-validate-msisdn-submittoken) - Validate ownership of a phone number.
- [`GET /_matrix/identity/v2/validate/msisdn/submitToken`](https://matrix.org/docs/spec/identity_service/r0.3.0#get-matrix-identity-v2-validate-msisdn-submittoken) - Validate ownership of a phone number.
- [`GET /_matrix/identity/v2/3pid/getValidated3pid`](https://matrix.org/docs/spec/identity_service/r0.3.0#get-matrix-identity-v2-3pid-getvalidated3pid) - Determines if a given 3pid has been validated by a user.
- [`POST /_matrix/identity/v2/3pid/bind`](https://matrix.org/docs/spec/identity_service/r0.3.0#post-matrix-identity-v2-3pid-bind) - Publish an association between a session and a Matrix user ID.
- [`POST /_matrix/identity/v2/3pid/unbind`](https://matrix.org/docs/spec/identity_service/r0.3.0#post-matrix-identity-v2-3pid-unbind) - Remove an association between a session and a Matrix user ID.
- [`POST /_matrix/identity/v2/store-invite`](https://matrix.org/docs/spec/identity_service/r0.3.0#post-matrix-identity-v2-store-invite) - Store pending invitations to a user's 3pid.
- [`POST /_matrix/identity/v2/sign-ed25519`](https://matrix.org/docs/spec/identity_service/r0.3.0#post-matrix-identity-v2-sign-ed25519) - Sign invitation details.
There is only one exception: [`POST /_matrix/identity/v2/terms`](https://matrix.org/docs/spec/identity_service/r0.3.0#post-matrix-identity-v2-terms) which uses for accepting the terms and requires the authorization.
## [Hash lookup](https://github.com/matrix-org/matrix-doc/blob/hs/hash-identity/proposals/2134-identity-hash-lookup.md)
Hashes and the pepper updates together according to the `rotationPolicy`.
```.yaml
hashing:
enabled: true # enable or disable the hash lookup MSC2140 (default is false)
pepperLength: 20 # length of the pepper value (default is 20)
rotationPolicy: per_requests # or `per_seconds` how often the hashes will be updating
hashStorageType: sql # or `in_memory` where the hashes will be stored
algorithms:
- none # the same as v1 bulk lookup
- sha256 # hash the 3PID and pepper.
delay: 2m # how often hashes will be updated if rotation policy = per_seconds (default is 10s)
requests: 10 # how many lookup requests will be performed before updating hashes if rotation policy = per_requests (default is 10)
```
When enabled and client requests the `none` algorithms then hash lookups works as v1 bulk lookup.
Delay specified in the format: `2d 4h 12m 34s` - this means 2 days 4 hours 12 minutes and 34 seconds. Zero units may be omitted. For example:
- 12s - 12 seconds
- 3m - 3 minutes
- 5m 6s - 5 minutes and 6 seconds
- 6h 3s - 6 hours and 3 seconds
Sha256 algorithm supports only sql, memory and exec 3PID providers.
For sql provider (i.e. for the `synapseSql`):
```.yaml
synapseSql:
lookup:
query: 'select user_id as mxid, medium, address from user_threepids' # query for retrive 3PIDs for hashes.
```
For general sql provider:
```.yaml
sql:
lookup:
query: 'select user as mxid, field1 as medium, field2 as address from some_table' # query for retrive 3PIDs for hashes.
```
Each query should return the `mxid`, `medium` and `address` fields.
For memory providers:
```.yaml
memory:
hashEnabled: true # enable the hash lookup (defaults is false)
```
For exec providers:
```.yaml
exec:
identity:
hashEnabled: true # enable the hash lookup (defaults is false)
```
NOTE: Federation requests work only with `none` algorithms.

View File

@@ -1,11 +1,8 @@
# Table of Contents # Table of Contents
- [Identity Concepts in Matrix](concepts.md)
- [Getting Started](getting-started.md)
- [Build from sources](build.md) (Optional) - [Build from sources](build.md) (Optional)
- Installation - Installation
- [Debian package](install/debian.md) - [Debian package](install/debian.md)
- [ArchLinux](install/archlinux.md) - [ArchLinux](install/archlinux.md)
- [NixOS](install/nixos.md)
- [Docker](install/docker.md) - [Docker](install/docker.md)
- [From source](install/source.md) - [From source](install/source.md)
- [Architecture overview](architecture.md) - [Architecture overview](architecture.md)
@@ -23,4 +20,3 @@
- [SendGrid](threepids/notification/sendgrid-handler.md) - [SendGrid](threepids/notification/sendgrid-handler.md)
- [Sessions](threepids/session/session.md) - [Sessions](threepids/session/session.md)
- [Views](threepids/session/session-views.md) - [Views](threepids/session/session-views.md)
- [FAQ](faq.md)

View File

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

View File

@@ -16,16 +16,19 @@ TCP 443
+<---------------------------------<+ +<---------------------------------<+
| |
| +-------------------+ | +-------------------+
TCP 8090 +-> | ma1sd | TCP 8090 +-> | mxisd |
| | | |
| - Profile's 3PIDs | | - Profile's 3PIDs >----+
| - 3PID Invites | | - 3PID Invites | | +--------------------------+
+-|-----------------+ +-|-----------------+ +>----------> | Central Identity service |
| | TCP 443 | Matrix.org / Vector.im |
| | +--------------------------+
+>-------------------->+
| |
TCP 443 TCP 443
| +------------------------+ | +------------------------+
| | Remote Federated | | | Remote Federated |
| | ma1sd servers | | | mxisd servers |
| | | | | |
+--> - 3PID Invites | +--> - 3PID Invites |
+------------------------+ +------------------------+

View File

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

View File

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

View File

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

View File

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

View File

@@ -3,7 +3,7 @@
- [Basic](#basic) - [Basic](#basic)
- [Overview](#overview) - [Overview](#overview)
- [synapse](#synapse) - [synapse](#synapse)
- [ma1sd](#ma1sd) - [mxisd](#mxisd)
- [Validate](#validate) - [Validate](#validate)
- [Next steps](#next-steps) - [Next steps](#next-steps)
- [Profile auto-fil](#profile-auto-fill) - [Profile auto-fil](#profile-auto-fill)
@@ -16,15 +16,15 @@
- [DNS Overwrite](#dns-overwrite) - [DNS Overwrite](#dns-overwrite)
## Description ## Description
Authentication is an enhanced feature of ma1sd to ensure coherent and centralized identity management. Authentication is an enhanced feature of mxisd to ensure coherent and centralized identity management.
It allows to use Identity stores configured in ma1sd to authenticate users on your Homeserver. It allows to use Identity stores configured in mxisd to authenticate users on your Homeserver.
Authentication is divided into two parts: Authentication is divided into two parts:
- [Basic](#basic): authenticate with a regular username. - [Basic](#basic): authenticate with a regular username.
- [Advanced](#advanced): same as basic with extra abilities like authenticate using a 3PID or do username rewrite. - [Advanced](#advanced): same as basic with extra ability to authenticate using a 3PID.
## Basic ## Basic
Authentication by username is possible by linking synapse and ma1sd together using a specific module for synapse, also Authentication by username is possible by linking synapse and mxisd together using a specific module for synapse, also
known as password provider. known as password provider.
### Overview ### Overview
@@ -33,7 +33,7 @@ An overview of the Basic Authentication process:
Identity stores Identity stores
Client +------+ Client +------+
| +-------------------------+ +--> | LDAP | | +-------------------------+ +--> | LDAP |
| +---------------+ /_matrix/identity | ma1sd | | +------+ | +---------------+ /_matrix/identity | mxisd | | +------+
+-> | Reverse proxy | >------------------+ | | | +-> | Reverse proxy | >------------------+ | | |
+--|------------+ | | | | +--------+ +--|------------+ | | | | +--------+
| +-----> Check ID stores >------+--> | SQL DB | | +-----> Check ID stores >------+--> | SQL DB |
@@ -49,20 +49,20 @@ An overview of the Basic Authentication process:
| user profiles | If valid credentials and supported by Identity store(s) | user profiles | If valid credentials and supported by Identity store(s)
+--------------------------+ +--------------------------+
``` ```
Performed on [synapse with REST auth module](https://github.com/ma1uta/matrix-synapse-rest-password-provider/blob/master/README.md) Performed on [synapse with REST auth module](https://github.com/kamax-io/matrix-synapse-rest-auth/blob/master/README.md)
### Synapse ### Synapse
- Install the [password provider](https://github.com/ma1uta/matrix-synapse-rest-password-provider) - Install the [password provider](https://github.com/kamax-io/matrix-synapse-rest-auth)
- Edit your **synapse** configuration: - Edit your **synapse** configuration:
- As described by the auth module documentation - As described by the auth module documentation
- Set `endpoint` to `http://ma1sdAddress:8090` - Replace `ma1sdAddress` by an IP/host name that provides a direct - Set `endpoint` to `http://mxisdAddress:8090` - Replace `mxisdAddress` by an IP/host name that provides a direct
connection to ma1sd. connection to mxisd.
This **MUST NOT** be a public address, and SHOULD NOT go through a reverse proxy. This **MUST NOT** be a public address, and SHOULD NOT go through a reverse proxy.
- Restart synapse - Restart synapse
### ma1sd ### mxisd
- Configure and enable at least one [Identity store](../stores/README.md) - Configure and enable at least one [Identity store](../stores/README.md)
- Restart ma1sd - Restart mxisd
### Validate ### Validate
Login on the Homeserver using credentials present in one of your Identity stores. Login on the Homeserver using credentials present in one of your Identity stores.
@@ -74,15 +74,7 @@ See your Identity store [documentation](../stores/README.md) on how to enable th
## Advanced ## Advanced
The Authentication feature allows users to: The Authentication feature allows users to login to their Homeserver by using their 3PIDs in a configured Identity store.
- Rewrite usernames matching a pattern to be mapped to another username via a 3PID.
- login to their Homeserver by using their 3PIDs in a configured Identity store.
This feature also allows to work around the following issues:
- Lowercase all usernames for synapse, allowing case-insensitive login
- Unable to login on synapse if username is numerical
- Any generic transformation of username prior to sending to synapse, bypassing the restriction that password providers
cannot change the localpart being authenticated.
### Overview ### Overview
This is performed by intercepting the Homeserver endpoint `/_matrix/client/r0/login` as depicted below: This is performed by intercepting the Homeserver endpoint `/_matrix/client/r0/login` as depicted below:
@@ -93,7 +85,7 @@ This is performed by intercepting the Homeserver endpoint `/_matrix/client/r0/lo
| | Step 1 +---------------------------+ Step 2 | | Step 1 +---------------------------+ Step 2
| | | | | | | |
Client+---->| /_matrix/client/r0/login +---------------->| | Look up address +---------+ Client+---->| /_matrix/client/r0/login +---------------->| | Look up address +---------+
| ^ | | ma1sd - Identity server +----------------->| Backend | | ^ | | mxisd - Identity server +----------------->| Backend |
| | | | | +---------+ | | | | | +---------+
| /_matrix/* +--+ +---------------------+ | | /_matrix/* +--+ +---------------------+ |
| | | +---------------+-----------+ | | | +---------------+-----------+
@@ -110,17 +102,17 @@ Client+---->| /_matrix/client/r0/login +---------------->|
``` ```
Steps of user authentication using a 3PID: Steps of user authentication using a 3PID:
1. The intercepted login request is directly sent to ma1sd instead of the Homeserver. 1. The intercepted login request is directly sent to mxisd instead of the Homeserver.
2. Identity stores are queried for a matching user identity in order to modify the request to use the user name. 2. Identity stores are queried for a matching user identity in order to modify the request to use the user name.
3. The Homeserver, from which the request was intercepted, is queried using the request at previous step. 3. The Homeserver, from which the request was intercepted, is queried using the request at previous step.
Its address is resolved using the DNS Overwrite feature to reach its internal address on a non-encrypted port. Its address is resolved using the DNS Overwrite feature to reach its internal address on a non-encrypted port.
4. The response from the Homeserver is sent back to the client, believing it was the HS which directly answered. 4. The response from the Homeserver is sent back to the client, believing it was the HS which directly answered.
### Requirements ### Requirements
- Compatible [Identity store](../stores/README.md)
- [Basic Authentication configured and working](#basic) - [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 - Reverse proxy setup
- Homeserver
- Compatible [Identity store](../stores/README.md)
### Configuration ### Configuration
#### Reverse Proxy #### Reverse Proxy
@@ -129,7 +121,7 @@ The specific configuration to put under the relevant `VirtualHost`:
```apache ```apache
ProxyPass /_matrix/client/r0/login http://localhost:8090/_matrix/client/r0/login ProxyPass /_matrix/client/r0/login http://localhost:8090/_matrix/client/r0/login
``` ```
`ProxyPreserveHost` or equivalent **must** be enabled to detect to which Homeserver ma1sd should talk to when building results. `ProxyPreserveHost` or equivalent **must** be enabled to detect to which Homeserver mxisd should talk to when building results.
Your VirtualHost should now look similar to: Your VirtualHost should now look similar to:
```apache ```apache
@@ -145,107 +137,18 @@ Your VirtualHost should now look similar to:
</VirtualHost> </VirtualHost>
``` ```
##### nginx
The specific configuration to add under the relevant `server`:
```nginx
location /_matrix/client/r0/login {
proxy_pass http://localhost:8090;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
}
```
Your `server` section should now look similar to:
```nginx
server {
listen 443 ssl;
server_name matrix.example.org;
# ...
location /_matrix/client/r0/login {
proxy_pass http://localhost:8090;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
}
location /_matrix/identity {
proxy_pass http://localhost:8090/_matrix/identity;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
}
location /_matrix {
proxy_pass http://localhost:8008/_matrix;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
}
}
```
#### DNS Overwrite #### DNS Overwrite
Just like you need to configure a reverse proxy to send client requests to mxisd, you also need to configure mxisd with
Just like you need to configure a reverse proxy to send client requests to ma1sd, you also need to configure ma1sd with
the internal IP of the Homeserver so it can talk to it directly to integrate its directory search. the internal IP of the Homeserver so it can talk to it directly to integrate its directory search.
To do so, put the following configuration in your ma1sd configuration: To do so, put the following configuration in your mxisd configuration:
```yaml ```yaml
dns: dns.overwrite.homeserver.client:
overwrite:
homeserver:
client:
- name: 'example.org' - name: 'example.org'
value: 'http://localhost:8008' value: 'http://localhost:8008'
``` ```
`name` must be the hostname of the URL that clients use when connecting to the Homeserver. `name` must be the hostname of the URL that clients use when connecting to the Homeserver.
You can use `${server.name}` to auto-populate the `value` using the `server.name` configuration option and avoid duplicating it. In case the hostname is the same as your Matrix domain, you can use `${matrix.domain}` to auto-populate the `value`
In case the hostname is the same as your Matrix domain and `server.name` is not explicitely set in the config, `server.name` will default to using the `matrix.domain` configuration option and avoid duplicating it.
`matrix.domain` and will still probably have the correct value.
`value` is the base internal URL of the Homeserver, without any `/_matrix/..` or trailing `/`. `value` is the base internal URL of the Homeserver, without any `/_matrix/..` or trailing `/`.
### Optional features
The following features are available after you have a working Advanced setup:
- Username rewrite: Allows you to rewrite the username of a regular login/pass authentication to a 3PID, that then gets resolved using the regular lookup process. Most common use case is to allow login with numerical usernames on synapse, which is not possible out of the box.
#### Username rewrite
In ma1sd config:
```yaml
auth:
rewrite:
user:
rules:
- regex: <your regexp>
medium: 'your.custom.medium.type'
```
`rules` takes a list of rules. Rules have two properties:
- `regexp`: The regex pattern to match. This **MUST** match the full string. See [Java regex](https://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html) for syntax.
- `medium`: Custom 3PID type that will be used in the 3PID lookup. This can be anything you want and needs to be supported
by your Identity store config and/or code.
Rules are matched in listed order.
Common regexp patterns:
- Numerical usernames: `[0-9]+`
##### LDAP Example
If your users use their numerical employee IDs, which cannot be used with synapse, you can make it work with (relevant config only):
```yaml
auth:
rewrite:
user:
rules:
- regex: '[0-9]+'
medium: 'kmx.employee.id'
ldap:
attribute:
threepid:
kmx.employee.id:
- 'ldapAttributeForEmployeeId'
```

View File

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

View File

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

View File

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

View File

@@ -1,16 +0,0 @@
# Profile
**WARNING**: The following sub-features are considered experimental and not officially supported. Use at your own peril.
## Public Profile enhancement
This feature allows to enhance a public profile query with more info than just Matrix ID and Display name, allowing for
custom applications to retrieve custom data not currently provided by synapse, per example.
**WARNING**: This information can be queried without authentication as per the specification. Do not enable unless in a
controlled environment.
### Configuration
#### Reverse proxy
##### Apache
```apache
ProxyPassMatch "^/_matrix/client/r0/profile/([^/]+)$" "http://127.0.0.1:8090/_matrix/client/r0/profile/$1"
```

View File

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

View File

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

View File

@@ -1,10 +1,9 @@
# Profile # Profile enhancement
The profile feature does not do anything on its own and acts as a support feature for others, allowing to retrieve **WARNING**: Alpha feature not officially supported. Do not use.
information about a user based on its Matrix ID by querying enabled [Identity stores](../stores/README.md).
Currently supported: ## Configuration
- Display name ### Reverse proxy
- 3PIDs #### Apache
- Roles/Groups ```apache
ProxyPassMatch "^/_matrix/client/r0/profile/([^/]+)$" "http://127.0.0.1:8090/_matrix/client/r0/profile/$1"
Experimental sub-features are also available. See [the dedicated document](experimental/profile.md). ```

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,14 +0,0 @@
---
** Outdated due to migrating to fork. **
---
# NixOS package
mxisd is available as a NixOS package in the official repos.
It is maintained by [maximilian](https://matrix.to/#/@maximilian:transformierende-gesellschaft.org), a community member.
Related resources:
- [NixOS](https://nixos.org/)
- [The module definition](https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/services/networking/mxisd.nix)

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,8 +1,7 @@
# Identity Stores # Identity Stores
- [Synapse](synapse.md) - Turn your SynapseDB into a self-contained Identity store - [Synapse](synapse.md)
- [LDAP-based](ldap.md) - Any LDAP-based product like Active Directory, Samba, NetIQ, OpenLDAP - [LDAP-based](ldap.md)
- [SQL Databases](sql.md) - Most common databases like MariaDB, MySQL, PostgreSQL, SQLite - [SQL Databases](sql.md)
- [Website / Web service / Web app](rest.md) - Arbitrary REST endpoints - [Website / Web service / Web app](rest.md)
- [Executables](exec.md) - Run arbitrary executables with configurable stdin, arguments, environment and stdout - [Google Firebase](firebase.md)
- [Wordpress](wordpress.md) - Connect your Wordpress-powered website DB - [Wordpress](wordpress.md)
- [Google Firebase](firebase.md) - Use your Firebase users (with experimental SSO support!)

View File

@@ -1,506 +0,0 @@
# Exec Identity Store
- [Features](#features)
- [Overview](#overview)
- [Configuration](#configuration)
- [Global](#global)
- [Tokens](#tokens)
- [Executable](#executable)
- [Input](#input)
- [Output](#output)
- [Examples](#examples)
- [Per-Feature](#per-feature)
- [Authentication](#authentication)
- [Tokens](#tokens-1)
- [Input](#input-1)
- [Output](#output-1)
- [Directory](#directory)
- [Tokens](#tokens-2)
- [Input](#input-2)
- [Output](#output-2)
- [Identity](#identity)
- [Single Lookup](#single-lookup)
- [Tokens](#tokens-3)
- [Input](#input-3)
- [Output](#output-3)
- [Bulk Lookup](#bulk-lookup)
- [Tokens](#tokens-4)
- [Input](#input-4)
- [Output](#output-4)
- [Profile](#profile)
- [Tokens](#tokens-5)
- [Input](#input-5)
- [Output](#output-5)
---
## Features
| Name | Supported |
|-------------------------------------------------|-----------|
| [Authentication](../features/authentication.md) | Yes |
| [Directory](../features/directory.md) | Yes |
| [Identity](../features/identity.md) | Yes |
| [Profile](../features/profile.md) | Yes |
This Identity Store lets you run arbitrary commands to handle the various requests in each support feature.
It is the most versatile Identity store of ma1sd, allowing you to connect any kind of logic with any executable/script.
## Overview
Each request can be mapping to a fully customizable command configuration.
The various parameters can be provided via any combination of:
- [Standard Input](https://en.wikipedia.org/wiki/Standard_streams#Standard_input_(stdin))
- [Command-line arguments](https://en.wikipedia.org/wiki/Command-line_interface#Arguments)
- [Environment variables](https://en.wikipedia.org/wiki/Environment_variable)
Each of those supports a set of customizable token which will be replaced prior to running the command, allowing to
provide the input values in any number of ways.
Success and data will be provided via any combination of:
- [Exit status](https://en.wikipedia.org/wiki/Exit_status)
- [Standard Output](https://en.wikipedia.org/wiki/Standard_streams#Standard_output_(stdout))
Each of those supports a set of configuration item to decide how to process the value and/or in which format.
All values, inputs and outputs are UTF-8 encoded.
## Configuration
Each feature comes with a set of possible lookup/action which is mapped to a generic configuration item block.
We will use the term `Executable` for each lookup/action and `Processor` for each configuration block.
### Global
```yaml
exec:
enabled: <boolean>
```
Enable/disable the Identity store at a global/default level. Each feature can still be individually enabled/disabled.
#### Tokens
The following options allow to globally set tokens for value replacement across all features and processors config.
Not all features use all tokens, and each feature might also have its own specific tokens. See each feature documentation.
They can be set within the following scope:
```yaml
exec:
token:
<token>: '<value>'
```
---
The following tokens and default values are available:
```yaml
localpart: '{localpart}'
```
Localpart of Matrix User IDs
```yaml
domain: '{domain}'
```
Domain of Matrix User IDs
```yaml
mxid: '{mxid}'
```
Full representation of Matrix User IDs
```yaml
medium: '{medium}'
```
Medium of 3PIDs
```yaml
address: '{address}'
```
Address of 3PIDs
```yaml
type: '{type}'
```
Type of query
```yaml
query: '{query}'
```
Query value
### Executable
*Executable*s have the following options:
```yaml
command: '/path/to/executableOrScript'
```
Set the executable (relative or absolute) path to be executed. If no command is given, the action will return a "neutral"
result if possible or be skipped altogether.
---
Command line arguments can be given via a list via both YAML formats:
```yaml
args:
- '-t'
- '{token}'
- '-v'
- 'value'
```
or
```yaml
args: ['-t', '{token}', '-v', 'value]
```
Each argument will be processed for token replacement.
---
Environment variables can be given as key/value pairs:
```yaml
env:
ENV_VAR_1: 'value'
ENV_VAR_2: '{token}'
```
Each variable value will be processed for token replacement.
#### Input
Standard input can be configured in the namespaces `input` with:
- `type`: The format to use
- `template`: The full or partial template with tokens to be used when generating the input
Not all features and *Executable*s allow for a template to be provided.
Templates for listed-based input are not supported at this time.
Default templates may be provided per *Executable*.
The following types are available:
- `json`: Use JSON format, shared with the [REST Identity Store](rest.md)
- `plain`: Use a custom multi-lines, optionally tab-separated input
#### Output
Standard output can be configured in the namespaces `output` with:
- `type`: The format to use
- `template`: The full or partial template with tokens to be used when processing the output
Not all features and *Executable*s allow for a template to be provided.
Templates for listed-based output are not supported at this time.
Default templates may be provided per *Executable*.
The following types are available:
- `json`: Use JSON format, shared with the [REST Identity Store](rest.md)
- `plain`: Use a custom multi-lines, optionally tab-separated output
### Examples
#### Basic
```yaml
exec:
auth:
enabled: true
command: '/opt/ma1sd-exec/auth.sh'
args: ['{localpart}']
input:
type: 'plain'
template: '{password}'
env:
DOMAIN: '{domain}'
```
With Authentication enabled, run `/opt/ma1sd-exec/auth.sh` when validating credentials, providing:
- A single command-line argument to provide the `localpart` as username
- A plain text string with the password token for standard input, which will be replaced by the password to check
- A single environment variable `DOMAIN` containing Matrix ID domain, if given
The command will use the default values for:
- Success exit status of `0`
- Failure exit status of `1`
- Any other exit status considered as error
- Standard output will not be processed
#### Advanced
Given the fictional `placeholder` feature:
```yaml
exec:
enabled: true
token:
mxid: '{matrixId}'
auth:
token:
localpart: '{username}'
command: '/path/to/executable'
args:
- '-u'
- '{username}'
env:
MATRIX_DOMAIN: '{domain}'
MATRIX_USER_ID: '{matrixId}'
output:
type: 'json'
exit:
success:
- 0
- 128
failure:
- 1
- 129
```
With:
- The Identity store enabled for all features
- A global specific token `{matrixId}` for Matrix User IDs, replacing the default `{mxid}`
Running `/path/to/executable` providing:
- A custom token for localpart, `{username}`, used as a 2nd command-line argument
- An extracted Matrix User ID `localpart` provided as the second command line argument, the first one being `-u`
- A password, the extracted Matrix `domain` and the full User ID as arbitrary environment variables, respectively
`PASSWORD`, `MATRIX_DOMAIN` and `MATRIX_USER_ID`
After execution:
- Process stdout as [JSON](https://en.wikipedia.org/wiki/JSON)
- Consider exit status `0` and `128` as success and try to process the stdout for data
- Consider exit status `1` and `129` as failure and try to process the stdout for error code and message
### Per Feature
See each dedicated [Feature](#features) section.
## Authentication
The Authentication feature can be enabled/disabled using:
```yaml
exec:
auth:
enabled: <true/false>
```
---
This feature provides a single *Executable* under the namespace:
```yaml
exec:
auth:
...
```
### Tokens
The following tokens/default values are specific to this feature:
```yaml
password: '{password}'
```
The provided password
### Input
Supported input types and default templates:
#### JSON (`json`)
Same as the [REST Identity Store](rest.md);
#### Plain (`plain`)
Default template:
```
{localpart}
{domain}
{mxid}
{password}
```
### Output
Supported output types and default templates:
#### JSON (`json`)
Same as the [REST Identity Store](rest.md);
#### Plain (`plain`)
**NOTE:** This has limited support. Use the JSON type for full support.
Default template:
```
[success status, true or 1 are interpreted as success]
[display name of the user]
```
## Directory
The Directory feature can be enabled/disabled using:
```yaml
exec:
directory:
enabled: <true/false>
```
---
Two search types configuration namespace are available, using the same input/output formats and templates:
By name:
```yaml
exec:
directory:
search:
byName:
...
```
By 3PID:
```yaml
exec:
directory:
search:
byThreepid:
...
```
#### Tokens
No specific tokens are available.
#### Input
Supported input types and default templates:
##### JSON (`json`)
Same as the [REST Identity Store](rest.md);
##### Plain (`plain`)
Default template:
```
[type of search, following the REST Identity store format]
[query string]
```
#### Output
Supported output types and default templates:
##### JSON (`json`)
Same as the [REST Identity Store](rest.md);
##### Plain (`plain`)
**Not supported at this time.** Use the JSON type.
## Identity
The Identity feature can be enabled/disabled using:
```yaml
exec.identity.enabled: <true/false>
```
### Single lookup
Configuration namespace:
```yaml
exec.identity.lookup.single:
...
```
#### Tokens
No specific tokens are available.
#### Input
Supported input types and default templates:
##### JSON (`json`)
Same as the [REST Identity Store](rest.md);
##### Plain (`plain`)
Default template:
```
{medium}
{address}
```
#### Output
Supported output types and default templates:
##### JSON (`json`)
Same as the [REST Identity Store](rest.md);
##### Plain (`plain`)
Default template:
```
[User ID type, as documented in the REST Identity Store]
[User ID value]
```
The User ID type will default to `localpart` if:
- Only one line is returned
- The first line is empty
### Bulk lookup
Configuration namespace:
```yaml
exec:
identity:
lookup:
bulk:
...
```
#### Tokens
No specific tokens are available.
#### Input
Supported input types and default templates:
##### JSON (`json`)
**NOTE:** Custom Templates are not supported.
Same as the [REST Identity Store](rest.md).
##### Plain (`plain`)
**Not supported at this time.** Use the JSON type.
#### Output
Supported output types and default templates:
##### JSON (`json`)
**NOTE:** Custom Templates are not supported.
Same as the [REST Identity Store](rest.md).
##### Plain (`plain`)
**Not supported at this time.** Use the JSON type.
## Profile
The Profile feature can be enabled/disabled using:
```yaml
exec:
profile:
enabled: <true/false>
```
---
The following *Executable*s namespace are available, share the same input/output formats and templates:
Get Display name:
```yaml
exec:
profile:
displayName:
...
```
Get 3PIDs:
```yaml
exec:
profile:
threePid:
...
```
Get Roles:
```yaml
exec:
profile:
role:
...
```
### Tokens
No specific tokens are available.
### Input
Supported input types and default templates:
#### JSON (`json`)
Same as the [REST Identity Store](rest.md);
#### Plain (`plain`)
Default template:
```
{localpart}
{domain}
{mxid}
```
### Output
Supported output types and default templates:
#### JSON (`json`)
Same as the [REST Identity Store](rest.md);
#### Plain (`plain`)
**Not supported at this time.** Use the JSON type.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,42 +1,86 @@
# Web pages for the 3PID sessions # Web pages for the 3PID sessions
You can customize the various pages used during a 3PID validation using the options below. You can customize the various pages used during a 3PID validation using [Thymeleaf templates](http://www.thymeleaf.org/).
## Configuration ## Configuration
Pseudo-configuration to illustrate the structure: Pseudo-configuration to illustrate the structure:
```yaml ```yaml
# CONFIGURATION EXAMPLE # CONFIGURATION EXAMPLE
# DO NOT COPY/PASTE THIS IN YOUR CONFIGURATION # DO NOT COPY/PASTE THIS IN YOUR CONFIGURATION
view: view.session.local:
session:
onTokenSubmit: onTokenSubmit:
success: '/path/to/session/tokenSubmitSuccess-page.html' success: '/path/to/session/local/tokenSubmitSuccess-page.html'
failure: '/path/to/session/tokenSubmitFailure-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'
# CONFIGURATION EXAMPLE # CONFIGURATION EXAMPLE
# DO NOT COPY/PASTE THIS IN YOUR CONFIGURATION # DO NOT COPY/PASTE THIS IN YOUR CONFIGURATION
``` ```
`view.session`: 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
This is triggered when a user submit a validation token for a 3PID session. It is typically visited when clicking the This is triggered when a user submit a validation token for a 3PID session. It is typically visited when clicking the
link in a validation email. link in a validation email.
The template should typically inform the user that the validation was successful and to go back in their Matrix client The template should typically inform the user that the validation was successful and to go back in their Matrix client
to finish the validation process, or that the validation failed. to finish the validation process.
Two configuration keys are available that accept paths to HTML templates: #### Placeholders
- `success`
- `failure`
### Serving static assets
ma1sd will not serve any static asset (images, JS, CSS, etc.). If such are needed, you will need to serve them using the
reverse proxy sitting in front of ma1sd using a path outside of the `/_matrix/identity/` namespace. We advise using
the base path `/static/` for such use cases, allowing to remain under the same hostname/origin.
You can also serve such assets using absolute URL, possibly under other domains, but be aware of Cross-Origin restrictions
in browsers which are out of scope of ma1sd.
## Placeholders
### Success
No object/placeholder are currently available. No object/placeholder are currently available.
### Failure ## 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
No object/placeholder are currently available. No object/placeholder are currently available.

View File

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

View File

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

Binary file not shown.

View File

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

22
gradlew vendored
View File

@@ -1,21 +1,5 @@
#!/usr/bin/env sh #!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
############################################################################## ##############################################################################
## ##
## Gradle start up script for UN*X ## Gradle start up script for UN*X
@@ -44,7 +28,7 @@ APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"` APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum" MAX_FD="maximum"
@@ -125,8 +109,8 @@ if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi fi
# For Cygwin or MSYS, switch paths to Windows format before running java # For Cygwin, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"` APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"` JAVACMD=`cygpath --unix "$JAVACMD"`

18
gradlew.bat vendored
View File

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

View File

@@ -1,150 +0,0 @@
# Sample configuration file explaining the minimum required keys to be set to run ma1sd
#
# For a complete list of options, see https://github.com/ma1uta/ma1sd/docs/README.md
#
# Please follow the Getting Started guide if this is your first time using/configuring ma1sd
#
# -- https://github.com/ma1uta/ma1sd/blob/master/docs/getting-started.md#getting-started
#
#######################
# Matrix config items #
#######################
# Matrix domain, same as the domain configure in your Homeserver configuration.
# NOTE: in Synapse Homeserver, the Matrix domain is defined as 'server_name' in configuration file.
#
# This is used to build the various identifiers in all the features.
#
# If the hostname of the public URL used to reach your Matrix services is different from your Matrix domain,
# per example matrix.domain.tld vs domain.tld, then use the server.name configuration option.
# See the "Configure" section of the Getting Started guide for more info.
#
matrix:
domain: ''
v1: true # deprecated
v2: true # MSC2140 API v2
################
# Signing keys #
################
# Absolute path for the Identity Server signing keys database.
# /!\ THIS MUST **NOT** BE YOUR HOMESERVER KEYS FILE /!\
# If this path does not exist, it will be auto-generated.
#
# During testing, /var/tmp/ma1sd/keys is a possible value
# For production, recommended location shall be one of the following:
# - /var/lib/ma1sd/keys
# - /var/opt/ma1sd/keys
# - /var/local/ma1sd/keys
#
key:
path: ''
# Path to the SQLite DB file for ma1sd internal storage
# /!\ THIS MUST **NOT** BE YOUR HOMESERVER DATABASE /!\
#
# Examples:
# - /var/opt/ma1sd/store.db
# - /var/local/ma1sd/store.db
# - /var/lib/ma1sd/store.db
#
storage:
provider:
sqlite:
database: '/path/to/ma1sd.db'
###################
# Identity Stores #
###################
# If you are using synapse standalone and do not have an Identity store,
# see https://github.com/ma1uta/ma1sd/blob/master/docs/stores/synapse.md#synapse-identity-store
#
# If you would like to integrate with your AD/Samba/LDAP server,
# see https://github.com/ma1uta/ma1sd/blob/master/docs/stores/ldap.md
#
# For any other Identity store, or to simply discover them,
# see https://github.com/ma1uta/ma1sd/blob/master/docs/stores/README.md
#################################################
# Notifications for invites/addition to profile #
#################################################
# This is mandatory to deal with anything e-mail related.
#
# For an introduction to sessions, invites and 3PIDs in general,
# see https://github.com/ma1uta/ma1sd/blob/master/docs/threepids/session/session.md#3pid-sessions
#
# If you would like to change the content of the notifications,
# see https://github.com/ma1uta/ma1sd/blob/master/docs/threepids/notification/template-generator.md
#
#### E-mail connector
threepid:
medium:
email:
identity:
# The e-mail to send as.
from: "matrix-identity@example.org"
connectors:
smtp:
# SMTP host
host: "smtp.example.org"
# TLS mode for the connection
# Possible values:
# 0 Disable any kind of TLS entirely
# 1 Enable STARTLS if supported by server (default)
# 2 Force STARTLS and fail if not available
# 3 Use full TLS/SSL instead of STARTLS
#
tls: 1
# SMTP port
# Be sure to adapt depending on your TLS choice, if changed from default
port: 587
# Login for SMTP
login: "matrix-identity@example.org"
# Password for the account
password: "ThePassword"
#### MSC2134 (hash lookup)
hashing:
enabled: false # enable or disable the hash lookup MSC2140 (default is false)
pepperLength: 20 # length of the pepper value (default is 20)
rotationPolicy: per_requests # or `per_seconds` how often the hashes will be updating
hashStorageType: sql # or `in_memory` where the hashes will be stored
algorithms:
- none # the same as v1 bulk lookup
- sha256 # hash the 3PID and pepper.
delay: 2m # how often hashes will be updated if rotation policy = per_seconds (default is 10s)
requests: 10 # how many lookup requests will be performed before updating hashes if rotation policy = per_requests (default is 10)
### hash lookup for synapseSql provider.
# synapseSql:
# lookup:
# query: 'select user_id as mxid, medium, address from user_threepids' # query for retrive 3PIDs for hashes.
#### MSC2140 (Terms)
policy:
policies:
term_name: # term name
version: 1.0 # version
terms:
en: # lang
name: term name en # localized name
url: https://ma1sd.host.tld/term_en.html # localized url
fe: # lang
name: term name fr # localized name
url: https://ma1sd.host.tld/term_fr.html # localized url
regexp:
- '/_matrix/identity/v2/account.*'
- '/_matrix/identity/v2/hash_details'
- '/_matrix/identity/v2/lookup'

View File

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

View File

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

View File

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

View File

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

View File

@@ -20,7 +20,8 @@
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ *
* */
package edazdarevic.commons.net; package edazdarevic.commons.net;
@@ -36,7 +37,6 @@ import java.util.List;
* both IPv4 and IPv6. * both IPv4 and IPv6.
*/ */
public class CIDRUtils { public class CIDRUtils {
private final String cidr; private final String cidr;
private InetAddress inetAddress; private InetAddress inetAddress;
@@ -44,6 +44,7 @@ public class CIDRUtils {
private InetAddress endAddress; private InetAddress endAddress;
private final int prefixLength; private final int prefixLength;
public CIDRUtils(String cidr) throws UnknownHostException { public CIDRUtils(String cidr) throws UnknownHostException {
this.cidr = cidr; this.cidr = cidr;
@@ -65,6 +66,7 @@ public class CIDRUtils {
private void calculate() throws UnknownHostException { private void calculate() throws UnknownHostException {
ByteBuffer maskBuffer; ByteBuffer maskBuffer;
int targetSize; int targetSize;
if (inetAddress.getAddress().length == 4) { if (inetAddress.getAddress().length == 4) {
@@ -118,9 +120,14 @@ public class CIDRUtils {
} }
public String getNetworkAddress() { public String getNetworkAddress() {
return this.startAddress.getHostAddress(); return this.startAddress.getHostAddress();
} }
public String getBroadcastAddress() {
return this.endAddress.getHostAddress();
}
public boolean isInRange(String ipAddress) throws UnknownHostException { public boolean isInRange(String ipAddress) throws UnknownHostException {
InetAddress address = InetAddress.getByName(ipAddress); InetAddress address = InetAddress.getByName(ipAddress);
BigInteger start = new BigInteger(1, this.startAddress.getAddress()); BigInteger start = new BigInteger(1, this.startAddress.getAddress());
@@ -132,5 +139,4 @@ public class CIDRUtils {
return (st == -1 || st == 0) && (te == -1 || te == 0); return (st == -1 || st == 0) && (te == -1 || te == 0);
} }
} }

View File

@@ -1,33 +0,0 @@
/*
* matrix-java-sdk - Matrix Client SDK for Java
* Copyright (C) 2018 Kamax Sarl
*
* https://www.kamax.io/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.matrix;
public class MalformedEventException extends MatrixException {
public MalformedEventException(String s) {
super("M_NOT_JSON", s);
}
public static MalformedEventException forId(String id) {
return new MalformedEventException("Event " + id + " is malformed");
}
}

View File

@@ -1,44 +0,0 @@
/*
* matrix-java-sdk - Matrix Client SDK for Java
* Copyright (C) 2017 Kamax Sarl
*
* https://www.kamax.io/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.matrix;
public class MatrixErrorInfo {
private String errcode;
private String error;
public MatrixErrorInfo(String errcode) {
this.errcode = errcode;
}
public MatrixErrorInfo(Throwable t) {
this.errcode = "AS_INTERNAL_SERVER_ERROR";
}
public String getErrcode() {
return errcode;
}
public String getError() {
return error;
}
}

View File

@@ -1,48 +0,0 @@
/*
* matrix-java-sdk - Matrix Client SDK for Java
* Copyright (C) 2018 Kamax Sarl
*
* https://www.kamax.io/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.matrix;
public class MatrixException extends RuntimeException {
private String errorCode;
private String error;
public MatrixException(String errorCode, String error) {
super(errorCode + ": " + error);
this.errorCode = errorCode;
this.error = error;
}
public MatrixException(String errorCode, String error, Throwable t) {
super(errorCode + ": " + error, t);
this.errorCode = errorCode;
this.error = error;
}
public String getErrorCode() {
return errorCode;
}
public String getError() {
return error;
}
}

View File

@@ -1,170 +0,0 @@
/*
* matrix-java-sdk - Matrix Client SDK for Java
* Copyright (C) 2017 Kamax Sarl
*
* https://www.kamax.io/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.matrix;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class MatrixID implements _MatrixID {
public static class Builder {
private MatrixID mxId;
public Builder(String id) {
mxId = MatrixID.parse(id);
}
public MatrixID valid() {
if (!mxId.isValid()) {
throw new IllegalArgumentException(mxId + " is not a valid Matrix ID");
}
return mxId;
}
public MatrixID acceptable() {
if (!mxId.isAcceptable()) {
throw new IllegalArgumentException(mxId + " is not an acceptable Matrix ID");
}
return mxId;
}
}
public static final String ALLOWED_CHARS = "0-9a-z-.=_/";
public static final Pattern LAX_PATTERN = Pattern.compile("@(.*?):(.+)");
public static final Pattern STRICT_PATTERN = Pattern.compile("@([" + ALLOWED_CHARS + "]+):(.+)");
private String id;
private String localpart;
private String domain;
private static String buildRaw(String localpart, String domain) {
return "@" + localpart + ":" + domain;
}
private static MatrixID parse(String id) {
Matcher m = LAX_PATTERN.matcher(id);
if (!m.matches()) {
throw new IllegalArgumentException(id + " is not a Matrix ID");
}
MatrixID mxId = new MatrixID();
mxId.id = id;
mxId.localpart = m.group(1);
mxId.domain = m.group(2);
return mxId;
}
public static Builder from(String id) {
return new Builder(id);
}
public static Builder from(String local, String domain) {
return from(buildRaw(local, domain));
}
public static MatrixID asValid(String id) {
return new Builder(id).valid();
}
public static MatrixID asAcceptable(String local, String domain) {
return from(local, domain).acceptable();
}
public static MatrixID asAcceptable(String id) {
return from(id).acceptable();
}
private MatrixID() {
// not for public consumption
}
private MatrixID(MatrixID mxId) {
this.id = mxId.id;
this.localpart = mxId.localpart;
this.domain = mxId.domain;
}
@Deprecated
public MatrixID(String mxId) {
this(parse(mxId));
}
@Deprecated
public MatrixID(String localpart, String domain) {
this(parse(buildRaw(localpart, domain)));
}
@Override
public String getId() {
return id;
}
@Override
public String getLocalPart() {
return localpart;
}
@Override
public String getDomain() {
return domain;
}
@Override
public MatrixID canonicalize() {
return parse(getId().toLowerCase());
}
@Override
public boolean isValid() {
return isAcceptable() && STRICT_PATTERN.matcher(id).matches();
}
@Override
public boolean isAcceptable() {
// TODO properly implement
return id.length() <= 255;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof MatrixID)) return false;
MatrixID matrixID = (MatrixID) o;
return id.equals(matrixID.id);
}
@Override
public int hashCode() {
return id.hashCode();
}
@Override
public String toString() {
return getId();
}
}

View File

@@ -1,93 +0,0 @@
/*
* matrix-java-sdk - Matrix Client SDK for Java
* Copyright (C) 2018 Kamax Sarl
*
* https://www.kamax.io/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.matrix;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import java.nio.charset.StandardCharsets;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static io.kamax.matrix.MatrixID.ALLOWED_CHARS;
public class MatrixIdCodec {
public static final String DELIMITER = "=";
public static final String ENCODE_REGEX = "[^" + ALLOWED_CHARS + "]+";
public static final Pattern ENCODE_PATTERN = Pattern.compile(ENCODE_REGEX);
public static final Pattern DECODE_PATTERN = Pattern.compile("(=[0-9a-f]{2})+");
private MatrixIdCodec() {
// not for public consumption
}
public static String encode(String decoded) {
decoded = decoded.toLowerCase();
StringBuilder builder = new StringBuilder();
for (Character c : decoded.toCharArray()) {
String s = c.toString();
Matcher lp = ENCODE_PATTERN.matcher(s);
if (!lp.find()) {
builder.append(s);
} else {
for (byte b : c.toString().getBytes(StandardCharsets.UTF_8)) {
builder.append(DELIMITER);
builder.append(Hex.encodeHexString(new byte[] { b }));
}
}
}
return builder.toString();
}
public static String decode(String encoded) {
StringBuilder builder = new StringBuilder();
Matcher m = DECODE_PATTERN.matcher(encoded);
int prevEnd = 0;
while (m.find()) {
try {
int start = m.start();
int end = m.end();
String sub = encoded.substring(start, end).replaceAll(DELIMITER, "");
String decoded = new String(Hex.decodeHex(sub.toCharArray()), StandardCharsets.UTF_8);
builder.append(encoded, prevEnd, start);
builder.append(decoded);
prevEnd = end - 1;
} catch (DecoderException e) {
e.printStackTrace();
}
}
prevEnd++;
if (prevEnd < encoded.length()) {
builder.append(encoded, prevEnd, encoded.length());
}
if (builder.length() == 0) {
return encoded;
} else {
return builder.toString();
}
}
}

View File

@@ -1,104 +0,0 @@
/*
* matrix-java-sdk - Matrix Client SDK for Java
* Copyright (C) 2018 Kamax Sàrl
*
* https://www.kamax.io/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.matrix;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
public class MatrixPath {
public static String encode(String element) {
try {
return URLEncoder.encode(element, StandardCharsets.UTF_8.name());
} catch (UnsupportedEncodingException e) {
// This would be madness if it happened, but we need to handle it still
throw new RuntimeException(e);
}
}
private static MatrixPath with(String base) {
return new MatrixPath().add(base);
}
public static MatrixPath root() {
return with("");
}
public static MatrixPath base() {
return root().add("_matrix");
}
public static MatrixPath client() {
return base().add("client");
}
public static MatrixPath clientR0() {
return client().add("r0");
}
private StringBuilder path = new StringBuilder();
/**
* Add the raw element to this path
*
* @param element
* The raw element to be added as is to the path, without encoding or path separator
* @return The MatrixPath
*/
public MatrixPath put(String element) {
path.append(element);
return this;
}
/**
* URL encode and add a new path element
*
* This method handle path separators
*
* @param element
* The element to be encoded and added.
* @return The MatrixPath
*/
public MatrixPath add(String element) {
// We add a path separator if this is the first character or if the last character is not a path separator
// already
if (path.length() == 0 || path.lastIndexOf("/", 0) < path.length() - 1) {
put("/");
}
put(encode(element));
return this;
}
public String get() {
return path.toString();
}
public String toString() {
return get();
}
public URI toURI() {
return URI.create(toString());
}
}

View File

@@ -1,61 +0,0 @@
/*
* matrix-java-sdk - Matrix Client SDK for Java
* Copyright (C) 2017 Kamax Sarl
*
* https://www.kamax.io/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.matrix;
public class ThreePid implements _ThreePid {
private String medium;
private String address;
public ThreePid(String medium, String address) {
this.medium = medium;
this.address = address;
}
@Override
public String getMedium() {
return medium;
}
@Override
public String getAddress() {
return address;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ThreePid threePid = (ThreePid) o;
if (!medium.equals(threePid.medium)) return false;
return address.equals(threePid.address);
}
@Override
public int hashCode() {
int result = medium.hashCode();
result = 31 * result + address.hashCode();
return result;
}
}

View File

@@ -1,61 +0,0 @@
/*
* matrix-java-sdk - Matrix Client SDK for Java
* Copyright (C) 2017 Kamax Sarl
*
* https://www.kamax.io/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.matrix;
public class ThreePidMapping implements _ThreePidMapping {
private _ThreePid threePid;
private _MatrixID mxId;
public ThreePidMapping(_ThreePid threePid, _MatrixID mxId) {
this.threePid = threePid;
this.mxId = mxId;
}
@Override
public _ThreePid getThreePid() {
return threePid;
}
@Override
public _MatrixID getMatrixId() {
return mxId;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ThreePidMapping that = (ThreePidMapping) o;
if (!threePid.equals(that.threePid)) return false;
return mxId.equals(that.mxId);
}
@Override
public int hashCode() {
int result = threePid.hashCode();
result = 31 * result + mxId.hashCode();
return result;
}
}

View File

@@ -1,46 +0,0 @@
/*
* matrix-java-sdk - Matrix Client SDK for Java
* Copyright (C) 2017 Kamax Sarl
*
* https://www.kamax.io/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.matrix;
public enum ThreePidMedium {
Email("email"),
PhoneNumber("msisdn");
private String id;
ThreePidMedium(String id) {
this.id = id;
}
public String getId() {
return id;
}
public String toString() {
return getId();
}
public boolean is(String s) {
return getId().contentEquals(s);
}
}

View File

@@ -1,41 +0,0 @@
/*
* matrix-java-sdk - Matrix Client SDK for Java
* Copyright (C) 2017 Kamax Sarl
*
* https://www.kamax.io/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.matrix;
import java.net.URI;
import java.net.URL;
import java.util.Optional;
public interface _MatrixContent {
URI getAddress();
URL getPermaLink();
boolean isValid();
Optional<String> getType();
byte[] getData();
Optional<String> getFilename();
}

View File

@@ -1,55 +0,0 @@
/*
* matrix-java-sdk - Matrix Client SDK for Java
* Copyright (C) 2017 Kamax Sarl
*
* https://www.kamax.io/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.matrix;
public interface _MatrixID {
String getId();
String getLocalPart();
String getDomain();
/**
* Render this Matrix ID strictly valid. In technical term, transform this ID so
* <code>isValid()</code> returns true.
*
* @return A canonical Matrix ID
*/
_MatrixID canonicalize();
/**
* If the Matrix ID is strictly valid in the protocol as per
* http://matrix.org/docs/spec/intro.html#user-identifiers
*
* @return true if strictly valid, false if not
*/
boolean isValid();
/**
* If the Matrix ID is acceptable in the protocol as per
* http://matrix.org/docs/spec/intro.html#historical-user-ids
*
* @return
*/
boolean isAcceptable();
}

View File

@@ -1,31 +0,0 @@
/*
* matrix-java-sdk - Matrix Client SDK for Java
* Copyright (C) 2017 Kamax Sarl
*
* https://www.kamax.io/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.matrix;
import io.kamax.matrix.client._Presence;
import java.util.Optional;
public interface _MatrixUser extends _MatrixUserProfile {
Optional<_Presence> getPresence();
}

View File

@@ -1,42 +0,0 @@
/*
* matrix-java-sdk - Matrix SDK for Java
* Copyright (C) 2018 Kamax Sarl
*
* https://www.kamax.io/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.matrix;
import java.net.URI;
import java.util.Optional;
public interface _MatrixUserProfile {
_MatrixID getId();
Optional<String> getName();
void setName(String name);
Optional<String> getAvatarUrl();
void setAvatar(String avatarRef);
void setAvatar(URI avatarUri);
Optional<_MatrixContent> getAvatar();
}

View File

@@ -1,29 +0,0 @@
/*
* matrix-java-sdk - Matrix Client SDK for Java
* Copyright (C) 2017 Kamax Sarl
*
* https://www.kamax.io/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.matrix;
public interface _ThreePid {
String getMedium();
String getAddress();
}

View File

@@ -1,29 +0,0 @@
/*
* matrix-java-sdk - Matrix Client SDK for Java
* Copyright (C) 2017 Kamax Sarl
*
* https://www.kamax.io/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.matrix;
public interface _ThreePidMapping {
_ThreePid getThreePid();
_MatrixID getMatrixId();
}

View File

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

View File

@@ -1,154 +0,0 @@
/*
* matrix-java-sdk - Matrix Client SDK for Java
* Copyright (C) 2017 Kamax Sarl
*
* https://www.kamax.io/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.matrix.client;
import io.kamax.matrix.MatrixID;
import io.kamax.matrix._MatrixID;
import io.kamax.matrix.hs.MatrixHomeserver;
import io.kamax.matrix.hs._MatrixHomeserver;
import java.net.URL;
import java.util.Objects;
import java.util.Optional;
public class MatrixClientContext {
private String domain;
private URL hsBaseUrl;
private URL isBaseUrl;
private _MatrixID user;
private String token;
private boolean isVirtual;
private String deviceId;
private String initialDeviceName;
public MatrixClientContext() {
// stub
}
public MatrixClientContext(MatrixClientContext other) {
this.domain = other.domain;
this.hsBaseUrl = other.hsBaseUrl;
this.isBaseUrl = other.isBaseUrl;
this.user = other.user;
this.token = other.token;
this.isVirtual = other.isVirtual;
this.deviceId = other.deviceId;
this.initialDeviceName = other.initialDeviceName;
}
public MatrixClientContext(_MatrixHomeserver hs) {
setDomain(hs.getDomain());
setHsBaseUrl(hs.getBaseEndpoint());
}
public MatrixClientContext(_MatrixHomeserver hs, _MatrixID user, String token) {
this(hs);
setUser(user);
setToken(token);
}
public _MatrixHomeserver getHomeserver() {
if (Objects.isNull(hsBaseUrl)) {
throw new IllegalStateException("Homeserver Base URL is not set");
}
return new MatrixHomeserver(domain, hsBaseUrl.toString());
}
public String getDomain() {
return domain;
}
public MatrixClientContext setDomain(String domain) {
this.domain = domain;
return this;
}
public URL getHsBaseUrl() {
return hsBaseUrl;
}
public MatrixClientContext setHsBaseUrl(URL hsBaseUrl) {
this.hsBaseUrl = hsBaseUrl;
return this;
}
public URL getIsBaseUrl() {
return isBaseUrl;
}
public MatrixClientContext setIsBaseUrl(URL isBaseUrl) {
this.isBaseUrl = isBaseUrl;
return this;
}
public Optional<_MatrixID> getUser() {
return Optional.ofNullable(user);
}
public MatrixClientContext setUser(_MatrixID user) {
this.user = user;
return this;
}
public MatrixClientContext setUserWithLocalpart(String localpart) {
setUser(MatrixID.asAcceptable(localpart, getDomain()));
return this;
}
public String getToken() {
return token;
}
public MatrixClientContext setToken(String token) {
this.token = token;
return this;
}
public boolean isVirtual() {
return isVirtual;
}
public MatrixClientContext setVirtual(boolean virtual) {
isVirtual = virtual;
return this;
}
public String getDeviceId() {
return deviceId;
}
public MatrixClientContext setDeviceId(String deviceId) {
this.deviceId = deviceId;
return this;
}
public String getInitialDeviceName() {
return initialDeviceName;
}
public MatrixClientContext setInitialDeviceName(String initialDeviceName) {
this.initialDeviceName = initialDeviceName;
return this;
}
}

View File

@@ -1,59 +0,0 @@
/*
* matrix-java-sdk - Matrix Client SDK for Java
* Copyright (C) 2018 Kamax Sarl
*
* https://www.kamax.io/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.matrix.client;
public class MatrixClientDefaults {
private int connectTimeout = 30 * 1000; // 30 sec
private int requestTimeout = 5 * 60 * 1000; // 5 min
private int socketTimeout = requestTimeout;
public int getConnectTimeout() {
return connectTimeout;
}
public MatrixClientDefaults setConnectTimeout(int connectTimeout) {
this.connectTimeout = connectTimeout;
return this;
}
public int getRequestTimeout() {
return requestTimeout;
}
public MatrixClientDefaults setRequestTimeout(int requestTimeout) {
this.requestTimeout = requestTimeout;
return this;
}
public int getSocketTimeout() {
return socketTimeout;
}
public MatrixClientDefaults setSocketTimeout(int socketTimeout) {
this.socketTimeout = socketTimeout;
return this;
}
}

View File

@@ -1,46 +0,0 @@
/*
* matrix-java-sdk - Matrix Client SDK for Java
* Copyright (C) 2017 Kamax Sarl
*
* https://www.kamax.io/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.matrix.client;
import io.kamax.matrix.MatrixErrorInfo;
import java.io.IOException;
import java.util.Optional;
public class MatrixClientRequestException extends RuntimeException {
private MatrixErrorInfo errorInfo;
public MatrixClientRequestException(IOException e) {
super(e);
}
public MatrixClientRequestException(MatrixErrorInfo errorInfo, String message) {
super(message);
this.errorInfo = errorInfo;
}
public Optional<MatrixErrorInfo> getError() {
return Optional.ofNullable(errorInfo);
}
}

View File

@@ -1,31 +0,0 @@
/*
* matrix-java-sdk - Matrix Client SDK for Java
* Copyright (C) 2018 Kamax Sarl
*
* https://www.kamax.io/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.matrix.client;
import io.kamax.matrix.MatrixErrorInfo;
public class MatrixClientRequestRateLimitedException extends MatrixClientRequestException {
public MatrixClientRequestRateLimitedException(MatrixErrorInfo errorInfo, String message) {
super(errorInfo, message);
}
}

View File

@@ -1,133 +0,0 @@
/*
* matrix-java-sdk - Matrix Client SDK for Java
* Copyright (C) 2017 Kamax Sarl
*
* https://www.kamax.io/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.matrix.client;
import io.kamax.matrix._MatrixContent;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.URI;
import java.net.URL;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.Optional;
import okhttp3.Request;
public class MatrixHttpContent extends AMatrixHttpClient implements _MatrixContent {
private Logger log = LoggerFactory.getLogger(MatrixHttpContent.class);
private final Pattern filenamePattern = Pattern.compile("filename=\"?([^\";]+)");
private URI address;
private MatrixHttpContentResult result;
private boolean loaded = false;
private boolean valid = false;
public MatrixHttpContent(MatrixClientContext context, URI address) {
super(context);
this.address = address;
}
// TODO switch a HTTP HEAD to fetch initial data, instead of loading in memory directly
private synchronized void load() {
if (loaded) {
return;
}
try {
if (!StringUtils.equalsIgnoreCase("mxc", address.getScheme())) {
log.debug("{} is not a supported protocol for avatars, ignoring", address.getScheme());
} else {
MatrixHttpRequest request = new MatrixHttpRequest(new Request.Builder().url(getPermaLink()));
result = executeContentRequest(request);
valid = result.isValid();
}
} catch (MatrixClientRequestException e) {
valid = false;
}
loaded = true;
}
@Override
public URI getAddress() {
return address;
}
@Override
public URL getPermaLink() {
return getMediaPathBuilder("download", address.getHost()).addEncodedPathSegments(address.getPath().substring(1))
.build().url();
}
@Override
public boolean isValid() {
load();
return valid;
}
@Override
public Optional<String> getType() {
load();
if (!isValid()) {
throw new IllegalStateException("This method should only be called, if valid is true.");
}
return result.getContentType();
}
@Override
public byte[] getData() {
load();
if (!isValid()) {
throw new IllegalStateException("This method should only be called, if valid is true.");
}
return result.getData();
}
@Override
public Optional<String> getFilename() {
load();
if (!isValid()) {
throw new IllegalStateException("This method should only be called, if valid is true.");
}
return result.getHeader("Content-Disposition").filter(l -> !l.isEmpty()).flatMap(l -> {
for (String v : l) {
Matcher m = filenamePattern.matcher(v);
if (m.find()) {
return Optional.of(m.group(1));
}
}
return Optional.empty();
});
}
}

View File

@@ -1,71 +0,0 @@
/*
* matrix-java-sdk - Matrix Client SDK for Java
* Copyright (C) 2017 Kamax Sarl
*
* https://www.kamax.io/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.matrix.client;
import java.io.IOException;
import java.util.*;
import java.util.Optional;
import okhttp3.MediaType;
import okhttp3.Response;
import okhttp3.ResponseBody;
public class MatrixHttpContentResult {
private final boolean valid;
private final Map<String, List<String>> headers;
private final Optional<String> contentType;
private final byte[] data;
public MatrixHttpContentResult(Response response) throws IOException {
try (ResponseBody body = response.body()) {
boolean hasBody = Objects.nonNull(body);
valid = hasBody && response.code() == 200;
headers = response.headers().toMultimap();
if (hasBody) {
contentType = Optional.ofNullable(body.contentType()).map(MediaType::toString);
data = body.bytes();
} else {
contentType = Optional.empty();
data = new byte[0];
}
}
}
public boolean isValid() {
return valid;
}
public Optional<List<String>> getHeader(String name) {
return Optional.ofNullable(headers.get(name));
}
public Optional<String> getContentType() {
return contentType;
}
public byte[] getData() {
return data;
}
}

View File

@@ -1,93 +0,0 @@
/*
* matrix-java-sdk - Matrix Client SDK for Java
* Copyright (C) 2018 Kamax Sarl
*
* https://www.kamax.io/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.matrix.client;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import io.kamax.matrix.json.GsonUtil;
import org.apache.commons.lang3.ArrayUtils;
import java.net.URL;
import java.util.List;
import javax.swing.*;
public class MatrixHttpPushRule extends AMatrixHttpClient implements _PushRule {
private static final String ActionKey = "actions";
private static final String EnabledKey = "enabled";
private final String[] baseSegments;
public MatrixHttpPushRule(MatrixClientContext context, String scope, String kind, String id) {
super(context);
baseSegments = new String[] { "pushrules", scope, kind, id };
}
private URL makeUrl() {
return getClientPath(baseSegments);
}
private URL makeUrl(String... segments) {
return getClientPath(ArrayUtils.addAll(baseSegments, segments));
}
@Override
public JsonObject getJson() {
return GsonUtil.parseObj(executeAuthenticated(getRequest(makeUrl())));
}
@Override
public void set(JsonObject data) {
executeAuthenticated(request(makeUrl()).put(getJsonBody(data)));
}
@Override
public void delete() {
executeAuthenticated(request(makeUrl()).delete());
}
@Override
public boolean isEnabled() {
JsonObject response = GsonUtil.parseObj(executeAuthenticated(getRequest(makeUrl(EnabledKey))));
return GsonUtil.getPrimitive(response, EnabledKey).getAsBoolean();
}
@Override
public void setEnabled(boolean enabled) {
executeAuthenticated(request(makeUrl(EnabledKey)).put(getJsonBody(GsonUtil.makeObj(EnabledKey, enabled))));
}
@Override
public List<String> getActions() {
JsonObject response = GsonUtil.parseObj(executeAuthenticated(getRequest(makeUrl(ActionKey))));
return GsonUtil.asList(GsonUtil.findArray(response, ActionKey).orElseGet(JsonArray::new), String.class);
}
@Override
public void setActions(List<String> data) {
executeAuthenticated(
request(makeUrl(ActionKey)).put(getJsonBody(GsonUtil.makeObj(ActionKey, GsonUtil.asArray(data)))));
}
}

View File

@@ -1,50 +0,0 @@
/*
* matrix-java-sdk - Matrix Client SDK for Java
* Copyright (C) 2017 Kamax Sarl
*
* https://www.kamax.io/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.matrix.client;
import java.util.ArrayList;
import java.util.List;
import okhttp3.Request;
public class MatrixHttpRequest {
private final Request.Builder httpRequest;
private List<Integer> ignoredErrorCodes = new ArrayList<>();
public MatrixHttpRequest(Request.Builder request) {
this.httpRequest = request;
}
public MatrixHttpRequest addIgnoredErrorCode(int errcode) {
ignoredErrorCodes.add(errcode);
return this;
}
public Request.Builder getHttpRequest() {
return httpRequest;
}
public List<Integer> getIgnoredErrorCodes() {
return ignoredErrorCodes;
}
}

View File

@@ -1,440 +0,0 @@
/*
* matrix-java-sdk - Matrix Client SDK for Java
* Copyright (C) 2017 Kamax Sarl
*
* https://www.kamax.io/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.matrix.client;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import io.kamax.matrix.MatrixErrorInfo;
import io.kamax.matrix.MatrixID;
import io.kamax.matrix._MatrixContent;
import io.kamax.matrix._MatrixID;
import io.kamax.matrix._MatrixUserProfile;
import io.kamax.matrix.hs._MatrixRoom;
import io.kamax.matrix.json.GsonUtil;
import io.kamax.matrix.json.RoomMessageChunkResponseJson;
import io.kamax.matrix.json.RoomMessageFormattedTextPutBody;
import io.kamax.matrix.json.RoomMessageTextPutBody;
import io.kamax.matrix.json.RoomTagSetBody;
import io.kamax.matrix.json.event.MatrixJsonPersistentEvent;
import io.kamax.matrix.room.*;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import okhttp3.HttpUrl;
import okhttp3.Request;
public class MatrixHttpRoom extends AMatrixHttpClient implements _MatrixRoom {
private Logger log = LoggerFactory.getLogger(MatrixHttpRoom.class);
private String roomId;
public MatrixHttpRoom(MatrixClientContext context, String roomId) {
super(context);
this.roomId = roomId;
}
@Override
public String getAddress() {
return roomId;
}
@Override
public Optional<String> getName() {
return getState("m.room.name").flatMap(obj -> GsonUtil.findString(obj, "name"));
}
@Override
public Optional<String> getTopic() {
return getState("m.room.topic").flatMap(obj -> GsonUtil.findString(obj, "topic"));
}
@Override
public Optional<String> getAvatarUrl() {
return getState("m.room.avatar").flatMap(obj -> GsonUtil.findString(obj, "url"));
}
@Override
public Optional<_MatrixContent> getAvatar() {
return getAvatarUrl().flatMap(url -> {
try {
return Optional.of(new MatrixHttpContent(context, new URI(url)));
} catch (URISyntaxException e) {
log.debug("{} is not a valid URI for avatar, returning empty", url);
return Optional.empty();
}
});
}
@Override
public String getId() {
return roomId;
}
@Override
public Optional<JsonObject> getState(String type) {
URL path = getClientPath("rooms", getAddress(), "state", type);
MatrixHttpRequest request = new MatrixHttpRequest(new Request.Builder().get().url(path));
request.addIgnoredErrorCode(404);
String body = executeAuthenticated(request);
if (StringUtils.isBlank(body)) {
return Optional.empty();
}
return Optional.of(GsonUtil.parseObj(body));
}
@Override
public Optional<JsonObject> getState(String type, String key) {
URL path = getClientPath("rooms", roomId, "state", type, key);
MatrixHttpRequest request = new MatrixHttpRequest(new Request.Builder().get().url(path));
request.addIgnoredErrorCode(404);
String body = executeAuthenticated(request);
if (StringUtils.isBlank(body)) {
return Optional.empty();
}
return Optional.of(GsonUtil.parseObj(body));
}
@Override
public void join() {
join(Collections.emptyList());
}
@Override
public void join(List<String> servers) {
HttpUrl.Builder b = getClientPathBuilder("rooms", roomId, "join");
servers.forEach(server -> b.addQueryParameter("server_name", server));
executeAuthenticated(new Request.Builder().post(getJsonBody(new JsonObject())).url(b.build()));
}
@Override
public Optional<MatrixErrorInfo> tryJoin() {
return tryJoin(Collections.emptyList());
}
@Override
public Optional<MatrixErrorInfo> tryJoin(List<String> servers) {
try {
join(servers);
return Optional.empty();
} catch (MatrixClientRequestException e) {
return e.getError();
}
}
@Override
public void leave() {
URL path = getClientPath("rooms", roomId, "leave");
MatrixHttpRequest request = new MatrixHttpRequest(
new Request.Builder().post(getJsonBody(new JsonObject())).url(path));
// TODO Find a better way to handle room objects for unknown rooms
// Maybe throw exception?
// TODO implement method to check room existence - isValid() ?
// if (res.getStatusLine().getStatusCode() == 404) {
// log.warn("Room {} is not joined, ignoring call", roomId);
// return;
// }
request.addIgnoredErrorCode(404);
executeAuthenticated(request);
}
@Override
public Optional<MatrixErrorInfo> tryLeave() {
try {
leave();
return Optional.empty();
} catch (MatrixClientRequestException e) {
return e.getError();
}
}
@Override
public void kick(_MatrixID user) {
kick(user, null);
}
@Override
public void kick(_MatrixID user, String reason) {
JsonObject body = new JsonObject();
body.addProperty("user_id", user.getId());
body.addProperty("reason", reason);
URL path = getClientPath("rooms", roomId, "kick");
MatrixHttpRequest request = new MatrixHttpRequest(new Request.Builder().post(getJsonBody(body)).url(path));
executeAuthenticated(request);
}
@Override
public Optional<MatrixErrorInfo> tryKick(_MatrixID user) {
return tryKick(user, null);
}
@Override
public Optional<MatrixErrorInfo> tryKick(_MatrixID user, String reason) {
try {
kick(user, reason);
return Optional.empty();
} catch (MatrixClientRequestException e) {
return e.getError();
}
}
@Override
public String sendEvent(String type, JsonObject content) {
// FIXME URL encoding
URL path = getClientPath("rooms", roomId, "send", type, Long.toString(System.currentTimeMillis()));
String body = executeAuthenticated(new Request.Builder().put(getJsonBody(content)).url(path));
return GsonUtil.getStringOrThrow(GsonUtil.parseObj(body), "event_id");
}
private String sendMessage(RoomMessageTextPutBody content) {
return sendEvent("m.room.message", GsonUtil.makeObj(content));
}
@Override
public String sendText(String message) {
return sendMessage(new RoomMessageTextPutBody(message));
}
@Override
public String sendFormattedText(String formatted, String rawFallback) {
// TODO sanitize input
return sendMessage(new RoomMessageFormattedTextPutBody(rawFallback, formatted));
}
@Override
public String sendNotice(String message) {
return sendMessage(new RoomMessageTextPutBody("m.notice", message));
}
@Override
public String sendNotice(String formatted, String plain) {
// TODO sanitize input
return sendMessage(new RoomMessageFormattedTextPutBody("m.notice", plain, formatted));
}
@Override
public void sendReceipt(String type, String eventId) {
URL path = getClientPath("rooms", roomId, "receipt", type, eventId);
executeAuthenticated(new Request.Builder().post(getJsonBody(new JsonObject())).url(path));
}
@Override
public void sendReceipt(ReceiptType type, String eventId) {
sendReceipt(type.getId(), eventId);
}
@Override
public void sendReadReceipt(String eventId) {
sendReceipt(ReceiptType.Read, eventId);
}
@Override
public void invite(_MatrixID mxId) {
URL path = getClientPath("rooms", roomId, "invite");
executeAuthenticated(
new Request.Builder().post(getJsonBody(GsonUtil.makeObj("user_id", mxId.getId()))).url(path));
}
@Override
public List<_MatrixUserProfile> getJoinedUsers() {
URL path = getClientPath("rooms", roomId, "joined_members");
String body = executeAuthenticated(new Request.Builder().get().url(path));
List<_MatrixUserProfile> ids = new ArrayList<>();
if (StringUtils.isNotEmpty(body)) {
JsonObject joinedUsers = jsonParser.parse(body).getAsJsonObject().get("joined").getAsJsonObject();
ids = joinedUsers.entrySet().stream().filter(e -> e.getValue().isJsonObject()).map(entry -> {
JsonObject obj = entry.getValue().getAsJsonObject();
return new MatrixHttpUser(getContext(), MatrixID.asAcceptable(entry.getKey())) {
@Override
public Optional<String> getName() {
return GsonUtil.findString(obj, "display_name");
}
@Override
public Optional<_MatrixContent> getAvatar() {
return GsonUtil.findString(obj, "avatar_url").flatMap(s -> {
try {
return Optional.of(new URI(s));
} catch (URISyntaxException e) {
return Optional.empty();
}
}).map(uri -> new MatrixHttpContent(getContext(), uri));
}
};
}).collect(Collectors.toList());
}
return ids;
}
@Override
public _MatrixRoomMessageChunk getMessages(_MatrixRoomMessageChunkOptions options) {
HttpUrl.Builder builder = getClientPathBuilder("rooms", roomId, "messages");
builder.addQueryParameter("from", options.getFromToken());
builder.addQueryParameter("dir", options.getDirection());
options.getToToken().ifPresent(token -> builder.addQueryParameter("to", token));
options.getLimit().ifPresent(limit -> builder.addQueryParameter("limit", limit.toString()));
String bodyRaw = executeAuthenticated(new Request.Builder().get().url(builder.build().url()));
RoomMessageChunkResponseJson body = GsonUtil.get().fromJson(bodyRaw, RoomMessageChunkResponseJson.class);
return new MatrixRoomMessageChunk(body.getStart(), body.getEnd(),
body.getChunk().stream().map(MatrixJsonPersistentEvent::new).collect(Collectors.toList()));
}
@Override
public List<Tag> getUserTags() {
return getAllTags().stream().filter(tag -> "u".equals(tag.getNamespace()))
.collect(Collectors.toList());
}
@Override
public List<Tag> getAllTags() {
URL path = getClientPath("user", getUserId(), "rooms", getAddress(), "tags");
String body = executeAuthenticated(new Request.Builder().get().url(path));
JsonObject jsonTags = GsonUtil.parseObj(body).getAsJsonObject("tags").getAsJsonObject();
List<Tag> tags = jsonTags.entrySet().stream().map(entry -> {
String completeName = entry.getKey();
String name = "";
String namespace = "";
if (completeName.startsWith("m.")) {
name = completeName.substring(2);
namespace = "m";
} else if (completeName.startsWith("u.")) {
name = completeName.substring(2);
namespace = "u";
} else {
name = completeName;
}
JsonElement jsonOrder = entry.getValue().getAsJsonObject().get("order");
Double order = null;
if (jsonOrder != null) {
order = jsonOrder.getAsDouble();
}
return new Tag(namespace, name, order);
}).collect(Collectors.toList());
return tags;
}
@Override
public void addUserTag(String tag) {
addTag("u." + tag, null);
}
@Override
public void addUserTag(String tag, double order) {
addTag("u." + tag, order);
}
@Override
public void deleteUserTag(String tag) {
deleteTag("u." + tag);
}
@Override
public void addFavouriteTag() {
addTag("m.favourite", null);
}
@Override
public void addFavouriteTag(double order) {
addTag("m.favourite", order);
}
@Override
public Optional<Tag> getFavouriteTag() {
return getAllTags().stream()
.filter(tag -> "m".equals(tag.getNamespace()) && "favourite".equals(tag.getName())).findFirst();
}
@Override
public void deleteFavouriteTag() {
deleteTag("m.favourite");
}
@Override
public void addLowpriorityTag() {
addTag("m.lowpriority", null);
}
@Override
public void addLowpriorityTag(double order) {
addTag("m.lowpriority", order);
}
@Override
public Optional<Tag> getLowpriorityTag() {
return getAllTags().stream()
.filter(tag -> "m".equals(tag.getNamespace()) && "lowpriority".equals(tag.getName())).findFirst();
}
@Override
public void deleteLowpriorityTag() {
deleteTag("m.lowpriority");
}
private void addTag(String tag, Double order) {
// TODO check name size
if (order != null && (order < 0 || order > 1)) {
throw new IllegalArgumentException("Order out of range!");
}
URL path = getClientPath("user", getUserId(), "rooms", getAddress(), "tags", tag);
Request.Builder request = new Request.Builder().url(path);
if (order != null) {
request.put(getJsonBody(new RoomTagSetBody(order)));
} else {
request.put(getJsonBody(new JsonObject()));
}
executeAuthenticated(request);
}
private void deleteTag(String tag) {
URL path = getClientPath("user", getUserId(), "rooms", getAddress(), "tags", tag);
executeAuthenticated(new Request.Builder().url(path).delete());
}
}

View File

@@ -1,125 +0,0 @@
/*
* matrix-java-sdk - Matrix Client SDK for Java
* Copyright (C) 2017 Kamax Sarl
*
* https://www.kamax.io/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.matrix.client;
import com.google.gson.JsonObject;
import io.kamax.matrix._MatrixContent;
import io.kamax.matrix._MatrixID;
import io.kamax.matrix._MatrixUser;
import io.kamax.matrix.client.regular.Presence;
import io.kamax.matrix.json.GsonUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Optional;
import okhttp3.Request;
public class MatrixHttpUser extends AMatrixHttpClient implements _MatrixUser {
private Logger log = LoggerFactory.getLogger(MatrixHttpUser.class);
private _MatrixID mxId;
public MatrixHttpUser(MatrixClientContext context, _MatrixID mxId) {
super(context);
this.mxId = mxId;
}
@Override
public _MatrixID getId() {
return mxId;
}
@Override
public Optional<String> getName() {
URL path = getClientPath("profile", mxId.getId(), "displayname");
MatrixHttpRequest request = new MatrixHttpRequest(new Request.Builder().get().url(path));
request.addIgnoredErrorCode(404);
String body = executeAuthenticated(request);
return extractAsStringFromBody(body, "displayname");
}
@Override
public void setName(String name) {
URL path = getClientPath("profile", mxId.getId(), "displayname");
JsonObject body = GsonUtil.makeObj("displayname", name);
executeAuthenticated(new Request.Builder().put(getJsonBody(body)).url(path));
}
@Override
public Optional<String> getAvatarUrl() {
URL path = getClientPath("profile", mxId.getId(), "avatar_url");
MatrixHttpRequest request = new MatrixHttpRequest(new Request.Builder().get().url(path));
request.addIgnoredErrorCode(404);
String body = executeAuthenticated(request);
return extractAsStringFromBody(body, "avatar_url");
}
@Override
public void setAvatar(String avatarRef) {
URL path = getClientPath("profile", mxId.getId(), "avatar_url");
JsonObject body = GsonUtil.makeObj("avatar_url", avatarRef);
executeAuthenticated(new Request.Builder().put(getJsonBody(body)).url(path));
}
@Override
public void setAvatar(URI avatarUri) {
setAvatar(avatarUri.toString());
}
@Override
public Optional<_MatrixContent> getAvatar() {
return getAvatarUrl().flatMap(uri -> {
try {
return Optional.of(new MatrixHttpContent(getContext(), new URI(uri)));
} catch (URISyntaxException e) {
log.debug("{} is not a valid URI for avatar, returning empty", uri);
return Optional.empty();
}
});
}
@Override
public Optional<_Presence> getPresence() {
URL path = getClientPath("presence", mxId.getId(), "status");
MatrixHttpRequest request = new MatrixHttpRequest(new Request.Builder().get().url(path));
request.addIgnoredErrorCode(404);
String body = execute(request);
if (StringUtils.isBlank(body)) {
return Optional.empty();
}
return Optional.of(new Presence(GsonUtil.parseObj(body)));
}
}

View File

@@ -1,39 +0,0 @@
/*
* matrix-java-sdk - Matrix Client SDK for Java
* Copyright (C) 2017 Kamax Sarl
*
* https://www.kamax.io/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.matrix.client;
public class MatrixPasswordCredentials {
private final String localPart;
private final String password;
public MatrixPasswordCredentials(String localPart, String password) {
this.localPart = localPart;
this.password = password;
}
public String getLocalPart() {
return localPart;
}
public String getPassword() {
return password;
}
}

View File

@@ -1,45 +0,0 @@
/*
* matrix-java-sdk - Matrix Client SDK for Java
* Copyright (C) 2018 Kamax Sàrl
*
* https://www.kamax.io/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.matrix.client;
import org.apache.commons.lang3.StringUtils;
public enum PresenceStatus {
Online("online"),
Offline("offline"),
Unavailable("unavailable");
private String id;
PresenceStatus(String id) {
this.id = id;
}
public String getId() {
return id;
}
public boolean is(String status) {
return StringUtils.equals(id, status);
}
}

View File

@@ -1,138 +0,0 @@
/*
* matrix-java-sdk - Matrix Client SDK for Java
* Copyright (C) 2018 Kamax Sarl
*
* https://www.kamax.io/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.matrix.client;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import io.kamax.matrix.json.GsonUtil;
import io.kamax.matrix.json.InvalidJsonException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
public class WellKnownAutoDiscoverySettings implements _AutoDiscoverySettings {
private final Logger log = LoggerFactory.getLogger(WellKnownAutoDiscoverySettings.class);
private JsonObject raw;
private List<URL> hsBaseUrls = new ArrayList<>();
private List<URL> isBaseUrls = new ArrayList<>();
/**
* Build .well-known auto-discovery settings from a .well-known source.
*
* @param raw
* The raw JSON data
* @throws IllegalArgumentException
* if the data is invalid and couldn't be parsed.
*/
public WellKnownAutoDiscoverySettings(String raw) {
try {
setRaw(GsonUtil.parseObj(raw));
} catch (JsonParseException | InvalidJsonException e) {
throw new IllegalArgumentException("Invalid JSON data for .well-known string");
}
}
private void setRaw(JsonObject raw) {
this.raw = raw;
process();
}
private Optional<URL> getUrl(String url) {
try {
return Optional.of(new URL(url));
} catch (MalformedURLException e) {
log.warn("Ignoring invalid Base URL entry in well-known: {} - {}", url, e.getMessage());
return Optional.empty();
}
}
private List<URL> getUrls(JsonArray array) {
List<URL> urls = new ArrayList<>();
array.forEach(el -> {
if (!el.isJsonPrimitive()) {
log.warn("Ignoring invalid Base URL entry in well-known: {} - Not a string", GsonUtil.get().toJson(el));
return;
}
getUrl(el.getAsString()).ifPresent(urls::add);
});
return urls;
}
private List<URL> processUrls(JsonObject base, String key) {
List<URL> urls = new ArrayList<>();
GsonUtil.findObj(base, key).ifPresent(cfg -> {
log.info("Found data");
GsonUtil.findArray(cfg, "base_urls").ifPresent(arr -> {
log.info("Found base URL(s)");
urls.addAll(getUrls(arr));
});
if (urls.isEmpty()) {
GsonUtil.findString(cfg, "base_url").flatMap(this::getUrl).ifPresent(urls::add);
}
});
return urls;
}
private void process() {
log.info("Processing Homeserver Base URLs");
hsBaseUrls = processUrls(raw, "m.homeserver");
log.info("Found {} valid URL(s)", hsBaseUrls.size());
log.info("Processing Identity server Base URLs");
isBaseUrls = processUrls(raw, "m.identity_server");
log.info("Found {} valid URL(s)", isBaseUrls.size());
}
@Override
public JsonObject getRaw() {
return raw;
}
@Override
public List<URL> getHsBaseUrls() {
return Collections.unmodifiableList(hsBaseUrls);
}
@Override
public List<URL> getIsBaseUrls() {
return Collections.unmodifiableList(isBaseUrls);
}
}

View File

@@ -1,36 +0,0 @@
/*
* matrix-java-sdk - Matrix Client SDK for Java
* Copyright (C) 2018 Kamax Sarl
*
* https://www.kamax.io/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.matrix.client;
import com.google.gson.JsonObject;
import java.net.URL;
import java.util.List;
public interface _AutoDiscoverySettings {
JsonObject getRaw();
List<URL> getHsBaseUrls();
List<URL> getIsBaseUrls();
}

View File

@@ -1,39 +0,0 @@
/*
* matrix-java-sdk - Matrix Client SDK for Java
* Copyright (C) 2018 Kamax Sarl
*
* https://www.kamax.io/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.matrix.client;
import com.google.gson.JsonObject;
import java.util.List;
public interface _GlobalPushRulesSet {
List<JsonObject> getContent();
List<JsonObject> getOverride();
List<JsonObject> getRoom();
List<JsonObject> getSender();
List<JsonObject> getUnderride();
}

View File

@@ -1,158 +0,0 @@
/*
* matrix-java-sdk - Matrix Client SDK for Java
* Copyright (C) 2017 Kamax Sarl
*
* https://www.kamax.io/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.matrix.client;
import com.google.gson.JsonObject;
import io.kamax.matrix._MatrixContent;
import io.kamax.matrix._MatrixID;
import io.kamax.matrix._MatrixUser;
import io.kamax.matrix.hs._MatrixRoom;
import io.kamax.matrix.room.RoomAlias;
import io.kamax.matrix.room._RoomAliasLookup;
import io.kamax.matrix.room._RoomCreationOptions;
import java.io.File;
import java.net.URI;
import java.util.List;
import java.util.Optional;
public interface _MatrixClient extends _MatrixClientRaw {
_MatrixID getWhoAmI();
void setDisplayName(String name);
_RoomAliasLookup lookup(RoomAlias alias);
_MatrixRoom createRoom(_RoomCreationOptions options);
_MatrixRoom getRoom(String roomId);
List<_MatrixRoom> getJoinedRooms();
_MatrixRoom joinRoom(String roomIdOrAlias);
_MatrixUser getUser(_MatrixID mxId);
Optional<String> getDeviceId();
/* Custom endpoint! */
// TODO refactor into custom synapse class?
void register(MatrixPasswordCredentials credentials, String sharedSecret, boolean admin);
/***
* Set the access token to use for any authenticated API calls.
*
* @param accessToken
* The access token provided by the server which must be valid
* @throws MatrixClientRequestException
* If an error occurred while checking for the identity behind the access token
*/
void setAccessToken(String accessToken) throws MatrixClientRequestException;
void login(MatrixPasswordCredentials credentials);
void logout();
_SyncData sync(_SyncOptions options);
/**
* Download content from the media repository
*
* @param mxUri
* The MXC URI for the content to download
* @return The content
* @throws IllegalArgumentException
* if the parameter is not a valid MXC URI
*/
_MatrixContent getMedia(String mxUri) throws IllegalArgumentException;
/**
* Download content from the media repository
*
* @param mxUri
* The MXC URI for the content to download
* @return The content
* @throws IllegalArgumentException
* if the parameter is not a valid MXC URI
*/
_MatrixContent getMedia(URI mxUri) throws IllegalArgumentException;
/**
* Upload content to the media repository
*
* @param data
* The data to send
* @param type
* The mime-type of the content upload
* @return The MXC URI for the uploaded content
*/
String putMedia(byte[] data, String type);
/**
* Upload content to the media repository
*
* @param data
* The data to send
* @param type
* The mime-type of the content upload
* @param filename
* A suggested filename for the content
* @return The MXC URI for the uploaded content
*/
String putMedia(byte[] data, String type, String filename);
/**
* Upload content to the media repository
*
* @param data
* The file to read the data from
* @param type
* The mime-type of the content upload
* @return The MXC URI for the uploaded content
*/
String putMedia(File data, String type);
/**
* Upload content to the media repository
*
* @param data
* The data to send
* @param type
* The mime-type of the content upload
* @param filename
* A suggested filename for the content
* @return The MXC URI for the uploaded content
*/
String putMedia(File data, String type, String filename);
List<JsonObject> getPushers();
void setPusher(JsonObject pusher);
void deletePusher(String pushKey);
_GlobalPushRulesSet getPushRules();
_PushRule getPushRule(String scope, String kind, String id);
}

View File

@@ -1,51 +0,0 @@
/*
* matrix-java-sdk - Matrix Client SDK for Java
* Copyright (C) 2017 Kamax Sarl
*
* https://www.kamax.io/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.matrix.client;
import io.kamax.matrix._MatrixID;
import io.kamax.matrix.hs._MatrixHomeserver;
import java.util.List;
import java.util.Optional;
public interface _MatrixClientRaw {
MatrixClientContext getContext();
_MatrixHomeserver getHomeserver();
Optional<String> getAccessToken();
Optional<_MatrixID> getUser();
Optional<_AutoDiscoverySettings> discoverSettings();
// FIXME
// we should maybe have a dedicated object for HS related items and be merged into getHomeserver() which is only
// holding state at this point and is not functional
List<String> getHomeApiVersions();
// FIXME
// we should maybe have a dedicated object for IS related items. Will reconsider when implementing
// other part of the IS API
boolean validateIsBaseUrl();
}

View File

@@ -1,29 +0,0 @@
/*
* matrix-java-sdk - Matrix Client SDK for Java
* Copyright (C) 2018 Kamax Sàrl
*
* https://www.kamax.io/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.matrix.client;
public interface _Presence {
String getStatus();
Long getLastActive();
}

View File

@@ -1,43 +0,0 @@
/*
* matrix-java-sdk - Matrix Client SDK for Java
* Copyright (C) 2018 Kamax Sarl
*
* https://www.kamax.io/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.matrix.client;
import com.google.gson.JsonObject;
import java.util.List;
public interface _PushRule {
JsonObject getJson();
void set(JsonObject data);
void delete();
boolean isEnabled();
void setEnabled(boolean enabled);
List<String> getActions();
void setActions(List<String> data);
}

View File

@@ -1,253 +0,0 @@
/*
* matrix-java-sdk - Matrix Client SDK for Java
* Copyright (C) 2018 Kamax Sarl
*
* https://www.kamax.io/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.matrix.client;
import com.google.gson.JsonObject;
import io.kamax.matrix.event._MatrixAccountDataEvent;
import io.kamax.matrix.event._MatrixEphemeralEvent;
import io.kamax.matrix.event._MatrixPersistentEvent;
import io.kamax.matrix.event._MatrixStateEvent;
import java.util.List;
import java.util.Set;
/**
* Representation of the data when performing a sync call on the Matrix Client API.
*/
public interface _SyncData {
interface State {
/**
* The state of the room.
*
* @return a list of state events.
*/
List<_MatrixStateEvent> getEvents();
}
interface Timeline {
/**
* Events that happened in the sync window.
*
* @return List of events.
*/
List<_MatrixPersistentEvent> getEvents();
/**
* If the number of events returned was limited by the sync filter.
*
* @return true if the number of events was limited, false if not.
*/
boolean isLimited();
/**
* Token that can be supplied to fetch previous events for the associated room.
*
* @return the token.
*/
String getPreviousBatchToken();
}
interface Ephemeral {
/**
* Events that happened in the sync window.
*
* @return List of events.
*/
List<_MatrixEphemeralEvent> getEvents();
}
interface AccountData {
/**
* Events that happened in the sync window.
*
* @return List of events.
*/
List<_MatrixAccountDataEvent> getEvents();
}
interface InvitedRoom {
/**
* The ID of the room the user was invited to.
*
* @return the room ID.
*/
String getId();
/**
* The state of the room at the invite event.
*
* @return a list of state events.
*/
State getState();
}
interface UnreadNotifications {
/**
* The number of unread notifications with the highlight flag set.
*
* @return the count.
*/
long getHighlightCount();
/**
* The total number of unread notifications.
*
* @return the count.
*/
long getNotificationCount();
}
interface JoinedRoom {
/**
* The ID of the room the user is joined to.
*
* @return the room id.
*/
String getId();
/**
* State changes prior the start of the timeline.
*
* @return a list of state events.
*/
State getState();
/**
* The room timeline for this sync batch.
*
* @return the timeline.
*/
Timeline getTimeline();
/**
* Ephemeral events of the room.
*
* @return a list of ephemeral events.
*/
Ephemeral getEphemeral();
/**
* Account events of the room.
*
* @return a list of account data events.
*/
AccountData getAccountData();
/**
* The Counts of unread notifications.
*
* @return unread notifications.
*/
UnreadNotifications getUnreadNotifications();
}
interface LeftRoom {
/**
* The ID of the room the user is joined to.
*
* @return the room id.
*/
String getId();
/**
* State changes prior the start of the timeline.
*
* @return a list of state events.
*/
State getState();
/**
* The room timeline up to the leave event.
*
* @return the timeline.
*/
Timeline getTimeline();
}
interface Rooms {
/**
* Rooms the user was invited to within this sync window.
*
* @return Set of InvitedRoom objects.
*/
Set<InvitedRoom> getInvited();
/**
* Rooms the user was joined in within this sync window.
*
* @return Set of JoinedRoom objects.
*/
Set<JoinedRoom> getJoined();
/**
* Rooms the user left from within this sync window.
*
* @return Set of LeftRoom objects.
*/
Set<LeftRoom> getLeft();
}
/**
* The batch token to supply in the next sync call.
*
* @return the batch token.
*/
String nextBatchToken();
/**
* The global private data created by this user.
*
* @return the account data.
*/
AccountData getAccountData();
/**
* Update to the rooms.
*
* @return rooms object.
*/
Rooms getRooms();
/**
* The raw JSON data for this object.
*
* @return the JSON data.
*/
JsonObject getJson();
}

View File

@@ -1,65 +0,0 @@
/*
* matrix-java-sdk - Matrix Client SDK for Java
* Copyright (C) 2018 Kamax Sarl
*
* https://www.kamax.io/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.matrix.client;
import java.util.Optional;
/**
* Possible options that can be passed to the sync call.
*/
public interface _SyncOptions {
/**
* The point of time to continue the sync from.
*
* @return A token that was provided in a previous sync call, if set.
*/
Optional<String> getSince();
/**
* The filter to use for the sync.
*
* @return The ID or raw JSON filter, if set.
*/
Optional<String> getFilter();
/**
* If the full state should be included in the sync.
*
* @return The full state option, if set.
*/
Optional<Boolean> withFullState();
/**
* If the client should automatically be marked as online.
*
* @return The set presence option, if set.
*/
Optional<String> getSetPresence();
/**
* The maximum time to wait, in milliseconds, before ending the sync call.
*
* @return The timeout option, if set.
*/
Optional<Long> getTimeout();
}

View File

@@ -1,87 +0,0 @@
/*
* matrix-java-sdk - Matrix Client SDK for Java
* Copyright (C) 2017 Kamax Sarl
*
* https://www.kamax.io/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.matrix.client.as;
import io.kamax.matrix.client.MatrixClientContext;
import io.kamax.matrix.client.MatrixClientDefaults;
import io.kamax.matrix.client._MatrixClient;
import io.kamax.matrix.client.regular.MatrixHttpClient;
import io.kamax.matrix.json.VirtualUserRegistrationBody;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.URL;
import okhttp3.OkHttpClient;
import okhttp3.Request;
public class MatrixApplicationServiceClient extends MatrixHttpClient implements _MatrixApplicationServiceClient {
private Logger log = LoggerFactory.getLogger(MatrixApplicationServiceClient.class);
public MatrixApplicationServiceClient(String domain) {
super(domain);
}
public MatrixApplicationServiceClient(URL hsBaseUrl) {
super(hsBaseUrl);
}
public MatrixApplicationServiceClient(MatrixClientContext context) {
super(context);
}
public MatrixApplicationServiceClient(MatrixClientContext context, OkHttpClient.Builder client) {
super(context, client);
}
public MatrixApplicationServiceClient(MatrixClientContext context, OkHttpClient.Builder client,
MatrixClientDefaults defaults) {
super(context, client, defaults);
}
public MatrixApplicationServiceClient(MatrixClientContext context, OkHttpClient client) {
super(context, client);
}
private MatrixHttpClient createClient(String localpart) {
MatrixClientContext context = new MatrixClientContext(getContext()).setUserWithLocalpart(localpart)
.setVirtual(true);
return new MatrixHttpClient(context);
}
@Override
public _MatrixClient createUser(String localpart) {
log.debug("Creating new user {}", localpart);
URL path = getClientPath("register");
executeAuthenticated(
new Request.Builder().post(getJsonBody(new VirtualUserRegistrationBody(localpart))).url(path));
return createClient(localpart);
}
@Override
public _MatrixClient getUser(String localpart) {
return createClient(localpart);
}
}

View File

@@ -1,31 +0,0 @@
/*
* matrix-java-sdk - Matrix Client SDK for Java
* Copyright (C) 2017 Kamax Sarl
*
* https://www.kamax.io/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.matrix.client.as;
import io.kamax.matrix.client._MatrixClient;
public interface _MatrixApplicationServiceClient extends _MatrixClient {
_MatrixClient createUser(String localpart);
_MatrixClient getUser(String localpart);
}

View File

@@ -1,68 +0,0 @@
/*
* matrix-java-sdk - Matrix Client SDK for Java
* Copyright (C) 2018 Kamax Sarl
*
* https://www.kamax.io/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.matrix.client.regular;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import io.kamax.matrix.client._GlobalPushRulesSet;
import io.kamax.matrix.json.GsonUtil;
import java.util.List;
public class GlobalPushRulesSet implements _GlobalPushRulesSet {
private JsonObject data;
public GlobalPushRulesSet(JsonObject data) {
this.data = data;
}
private List<JsonObject> getForKey(String key) {
return GsonUtil.asList(GsonUtil.findArray(data, key).orElseGet(JsonArray::new), JsonObject.class);
}
@Override
public List<JsonObject> getContent() {
return getForKey("content");
}
@Override
public List<JsonObject> getOverride() {
return getForKey("override");
}
@Override
public List<JsonObject> getRoom() {
return getForKey("room");
}
@Override
public List<JsonObject> getSender() {
return getForKey("sender");
}
@Override
public List<JsonObject> getUnderride() {
return getForKey("underride");
}
}

View File

@@ -1,292 +0,0 @@
/*
* matrix-java-sdk - Matrix Client SDK for Java
* Copyright (C) 2017 Kamax Sarl
*
* https://www.kamax.io/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.matrix.client.regular;
import com.google.gson.JsonNull;
import com.google.gson.JsonObject;
import io.kamax.matrix.MatrixID;
import io.kamax.matrix._MatrixContent;
import io.kamax.matrix._MatrixID;
import io.kamax.matrix._MatrixUser;
import io.kamax.matrix.client.*;
import io.kamax.matrix.hs._MatrixRoom;
import io.kamax.matrix.json.*;
import io.kamax.matrix.room.RoomAlias;
import io.kamax.matrix.room.RoomAliasLookup;
import io.kamax.matrix.room._RoomAliasLookup;
import io.kamax.matrix.room._RoomCreationOptions;
import org.apache.commons.codec.digest.HmacAlgorithms;
import org.apache.commons.codec.digest.HmacUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.net.URI;
import java.net.URL;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import okhttp3.*;
public class MatrixHttpClient extends AMatrixHttpClient implements _MatrixClient {
private Logger log = LoggerFactory.getLogger(MatrixHttpClient.class);
public MatrixHttpClient(String domain) {
super(domain);
}
public MatrixHttpClient(URL hsBaseUrl) {
super(hsBaseUrl);
}
public MatrixHttpClient(MatrixClientContext context) {
super(context);
}
public MatrixHttpClient(MatrixClientContext context, OkHttpClient.Builder client) {
super(context, client);
}
public MatrixHttpClient(MatrixClientContext context, OkHttpClient.Builder client, MatrixClientDefaults defaults) {
super(context, client, defaults);
}
public MatrixHttpClient(MatrixClientContext context, OkHttpClient client) {
super(context, client);
}
protected _MatrixID getIdentity(String token) {
URL path = getClientPath("account", "whoami");
String body = executeAuthenticated(new Request.Builder().get().url(path), token);
return MatrixID.from(GsonUtil.getStringOrThrow(GsonUtil.parseObj(body), "user_id")).acceptable();
}
@Override
public _MatrixID getWhoAmI() {
URL path = getClientPath("account", "whoami");
String body = executeAuthenticated(new Request.Builder().get().url(path));
return MatrixID.from(GsonUtil.getStringOrThrow(GsonUtil.parseObj(body), "user_id")).acceptable();
}
@Override
public void setDisplayName(String name) {
URL path = getClientPath("profile", getUserId(), "displayname");
execute(new Request.Builder().put(getJsonBody(new UserDisplaynameSetBody(name))).url(path));
}
@Override
public _RoomAliasLookup lookup(RoomAlias alias) {
URL path = getClientPath("directory", "room", alias.getId());
String resBody = execute(new Request.Builder().get().url(path));
RoomAliasLookupJson lookup = GsonUtil.get().fromJson(resBody, RoomAliasLookupJson.class);
return new RoomAliasLookup(lookup.getRoomId(), alias.getId(), lookup.getServers());
}
@Override
public _MatrixRoom createRoom(_RoomCreationOptions options) {
URL path = getClientPath("createRoom");
String resBody = executeAuthenticated(
new Request.Builder().post(getJsonBody(new RoomCreationRequestJson(options))).url(path));
String roomId = GsonUtil.get().fromJson(resBody, RoomCreationResponseJson.class).getRoomId();
return getRoom(roomId);
}
@Override
public _MatrixRoom getRoom(String roomId) {
return new MatrixHttpRoom(getContext(), roomId);
}
@Override
public List<_MatrixRoom> getJoinedRooms() {
URL path = getClientPath("joined_rooms");
JsonObject resBody = GsonUtil.parseObj(executeAuthenticated(new Request.Builder().get().url(path)));
return GsonUtil.asList(resBody, "joined_rooms", String.class).stream().map(this::getRoom)
.collect(Collectors.toList());
}
@Override
public _MatrixRoom joinRoom(String roomIdOrAlias) {
URL path = getClientPath("join", roomIdOrAlias);
String resBody = executeAuthenticated(new Request.Builder().post(getJsonBody(new JsonObject())).url(path));
String roomId = GsonUtil.get().fromJson(resBody, RoomCreationResponseJson.class).getRoomId();
return getRoom(roomId);
}
@Override
public _MatrixUser getUser(_MatrixID mxId) {
return new MatrixHttpUser(getContext(), mxId);
}
@Override
public Optional<String> getDeviceId() {
return Optional.ofNullable(context.getDeviceId());
}
protected void updateContext(String resBody) {
LoginResponse response = gson.fromJson(resBody, LoginResponse.class);
context.setToken(response.getAccessToken());
context.setDeviceId(response.getDeviceId());
context.setUser(MatrixID.asAcceptable(response.getUserId()));
}
@Override
public void register(MatrixPasswordCredentials credentials, String sharedSecret, boolean admin) {
// As per synapse registration script:
// https://github.com/matrix-org/synapse/blob/master/scripts/register_new_matrix_user#L28
String value = credentials.getLocalPart() + "\0" + credentials.getPassword() + "\0"
+ (admin ? "admin" : "notadmin");
String mac = new HmacUtils(HmacAlgorithms.HMAC_SHA_1, sharedSecret).hmacHex(value);
JsonObject body = new JsonObject();
body.addProperty("user", credentials.getLocalPart());
body.addProperty("password", credentials.getPassword());
body.addProperty("mac", mac);
body.addProperty("type", "org.matrix.login.shared_secret");
body.addProperty("admin", false);
URL url = getPath("client", "api", "v1", "register");
updateContext(execute(new Request.Builder().post(getJsonBody(body)).url(url)));
}
@Override
public void setAccessToken(String accessToken) {
context.setUser(getIdentity(accessToken));
context.setToken(accessToken);
}
@Override
public void login(MatrixPasswordCredentials credentials) {
URL url = getClientPath("login");
LoginPostBody data = new LoginPostBody(credentials.getLocalPart(), credentials.getPassword());
getDeviceId().ifPresent(data::setDeviceId);
Optional.ofNullable(context.getInitialDeviceName()).ifPresent(data::setInitialDeviceDisplayName);
updateContext(execute(new Request.Builder().post(getJsonBody(data)).url(url)));
}
@Override
public void logout() {
URL path = getClientPath("logout");
executeAuthenticated(new Request.Builder().post(getJsonBody(new JsonObject())).url(path));
context.setToken(null);
context.setUser(null);
context.setDeviceId(null);
}
@Override
public _SyncData sync(_SyncOptions options) {
long start = System.currentTimeMillis();
HttpUrl.Builder path = getClientPathBuilder("sync");
path.addQueryParameter("timeout", options.getTimeout().map(Long::intValue).orElse(30000).toString());
options.getSince().ifPresent(since -> path.addQueryParameter("since", since));
options.getFilter().ifPresent(filter -> path.addQueryParameter("filter", filter));
options.withFullState().ifPresent(state -> path.addQueryParameter("full_state", state ? "true" : "false"));
options.getSetPresence().ifPresent(presence -> path.addQueryParameter("presence", presence));
String body = executeAuthenticated(new Request.Builder().get().url(path.build().url()));
long request = System.currentTimeMillis();
log.info("Sync: network request took {} ms", (request - start));
SyncDataJson data = new SyncDataJson(GsonUtil.parseObj(body));
long parsing = System.currentTimeMillis();
log.info("Sync: parsing took {} ms", (parsing - request));
return data;
}
@Override
public _MatrixContent getMedia(String mxUri) throws IllegalArgumentException {
return getMedia(URI.create(mxUri));
}
@Override
public _MatrixContent getMedia(URI mxUri) throws IllegalArgumentException {
return new MatrixHttpContent(context, mxUri);
}
private String putMedia(Request.Builder builder, String filename) {
HttpUrl.Builder b = getMediaPathBuilder("upload");
if (StringUtils.isNotEmpty(filename)) b.addQueryParameter("filename", filename);
String body = executeAuthenticated(builder.url(b.build()));
return GsonUtil.getStringOrThrow(GsonUtil.parseObj(body), "content_uri");
}
@Override
public String putMedia(byte[] data, String type) {
return putMedia(data, type, null);
}
@Override
public String putMedia(byte[] data, String type, String filename) {
return putMedia(new Request.Builder().post(RequestBody.create(MediaType.parse(type), data)), filename);
}
@Override
public String putMedia(File data, String type) {
return putMedia(data, type, null);
}
@Override
public String putMedia(File data, String type, String filename) {
return putMedia(new Request.Builder().post(RequestBody.create(MediaType.parse(type), data)), filename);
}
@Override
public List<JsonObject> getPushers() {
URL url = getClientPath("pushers");
JsonObject response = GsonUtil.parseObj(executeAuthenticated(new Request.Builder().get().url(url)));
return GsonUtil.findArray(response, "pushers").map(array -> GsonUtil.asList(array, JsonObject.class))
.orElse(Collections.emptyList());
}
@Override
public void setPusher(JsonObject pusher) {
URL url = getClientPath("pushers", "set");
executeAuthenticated(new Request.Builder().url(url).post(getJsonBody(pusher)));
}
@Override
public void deletePusher(String pushKey) {
JsonObject pusher = new JsonObject();
pusher.add("kind", JsonNull.INSTANCE);
pusher.addProperty("pushkey", pushKey);
setPusher(pusher);
}
@Override
public _GlobalPushRulesSet getPushRules() {
URL url = getClientPath("pushrules", "global", "");
JsonObject response = GsonUtil.parseObj(executeAuthenticated(new Request.Builder().url(url).get()));
return new GlobalPushRulesSet(response);
}
@Override
public _PushRule getPushRule(String scope, String kind, String id) {
return new MatrixHttpPushRule(context, scope, kind, id);
}
}

View File

@@ -1,48 +0,0 @@
/*
* matrix-java-sdk - Matrix Client SDK for Java
* Copyright (C) 2018 Kamax Sàrl
*
* https://www.kamax.io/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.matrix.client.regular;
import com.google.gson.JsonObject;
import io.kamax.matrix.client._Presence;
import io.kamax.matrix.json.GsonUtil;
public class Presence implements _Presence {
private String status;
private Long lastActive;
public Presence(JsonObject json) {
this.status = GsonUtil.getStringOrThrow(json, "presence");
this.lastActive = GsonUtil.getLong(json, "last_active_ago");
}
@Override
public String getStatus() {
return status;
}
@Override
public Long getLastActive() {
return lastActive;
}
}

View File

@@ -1,390 +0,0 @@
/*
* matrix-java-sdk - Matrix Client SDK for Java
* Copyright (C) 2018 Kamax Sarl
*
* https://www.kamax.io/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.matrix.client.regular;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import io.kamax.matrix.MatrixID;
import io.kamax.matrix._MatrixID;
import io.kamax.matrix.client._SyncData;
import io.kamax.matrix.event.EventKey;
import io.kamax.matrix.event._MatrixAccountDataEvent;
import io.kamax.matrix.event._MatrixEphemeralEvent;
import io.kamax.matrix.event._MatrixPersistentEvent;
import io.kamax.matrix.event._MatrixStateEvent;
import io.kamax.matrix.json.MatrixJsonObject;
import java.util.*;
public class SyncDataJson extends MatrixJsonObject implements _SyncData {
public class MatrixPersistentEventJson extends MatrixJsonObject implements _MatrixPersistentEvent {
public MatrixPersistentEventJson(JsonObject obj) {
super(obj);
}
@Override
public String getId() {
return findString(EventKey.Id.get()).orElse(""); // FIXME refactor event structure
}
@Override
public String getType() {
return getString(EventKey.Type.get());
}
@Override
public Long getTime() {
return getLong(EventKey.Timestamp.get());
}
@Override
public _MatrixID getSender() {
return MatrixID.from(getString(EventKey.Sender.get())).acceptable();
}
}
public class MatrixEphemeralEventJson extends MatrixJsonObject implements _MatrixEphemeralEvent {
public MatrixEphemeralEventJson(JsonObject obj) {
super(obj);
}
@Override
public String getType() {
return getString(EventKey.Type.get());
}
}
public class MatrixAccountDataEventJson extends MatrixJsonObject implements _MatrixAccountDataEvent {
public MatrixAccountDataEventJson(JsonObject obj) {
super(obj);
}
@Override
public String getType() {
return getString(EventKey.Type.get());
}
}
public class MatrixStateEventJson extends MatrixPersistentEventJson implements _MatrixStateEvent {
public MatrixStateEventJson(JsonObject obj) {
super(obj);
}
@Override
public String getStateKey() {
return getString(EventKey.StateKey.get());
}
}
public class StateJson extends MatrixJsonObject implements _SyncData.State {
private List<_MatrixStateEvent> events = new ArrayList<>();
public StateJson(JsonObject obj) {
super(obj);
findArray("events").ifPresent(array -> {
for (JsonElement el : array) {
events.add(new MatrixStateEventJson(asObj(el)));
}
});
}
@Override
public List<_MatrixStateEvent> getEvents() {
return events;
}
}
public class TimelineJson extends MatrixJsonObject implements _SyncData.Timeline {
private List<_MatrixPersistentEvent> events = new ArrayList<>();
public TimelineJson(JsonObject obj) {
super(obj);
findArray("events").ifPresent(array -> {
for (JsonElement el : array) {
events.add(new MatrixPersistentEventJson(asObj(el)));
}
});
}
@Override
public List<_MatrixPersistentEvent> getEvents() {
return events;
}
@Override
public boolean isLimited() {
return findString("limited").map("true"::equals).orElse(false);
}
@Override
public String getPreviousBatchToken() {
return getString("prev_batch");
}
}
public class EphemeralJson extends MatrixJsonObject implements _SyncData.Ephemeral {
private List<_MatrixEphemeralEvent> events = new ArrayList<>();
public EphemeralJson(JsonObject obj) {
super(obj);
findArray("events").ifPresent(array -> {
for (JsonElement el : array) {
events.add(new MatrixEphemeralEventJson(asObj(el)));
}
});
}
@Override
public List<_MatrixEphemeralEvent> getEvents() {
return events;
}
}
public class AccountDataJson extends MatrixJsonObject implements _SyncData.AccountData {
private List<_MatrixAccountDataEvent> events = new ArrayList<>();
public AccountDataJson(JsonObject obj) {
super(obj);
findArray("events").ifPresent(array -> {
for (JsonElement el : array) {
events.add(new MatrixAccountDataEventJson(asObj(el)));
}
});
}
@Override
public List<_MatrixAccountDataEvent> getEvents() {
return events;
}
}
public class InvitedRoomJson extends MatrixJsonObject implements _SyncData.InvitedRoom {
private String id;
private State state;
public InvitedRoomJson(String id, JsonObject data) {
super(data);
this.id = id;
this.state = new StateJson(findObj("invite_state").orElseGet(JsonObject::new));
}
@Override
public String getId() {
return id;
}
@Override
public State getState() {
return state;
}
}
public class UnreadNotificationsJson extends MatrixJsonObject implements _SyncData.UnreadNotifications {
private long highlights;
private long global;
public UnreadNotificationsJson(JsonObject data) {
super(data);
this.highlights = findLong("highlight_count").orElse(0L);
this.global = findLong("notification_count").orElse(0L);
}
@Override
public long getHighlightCount() {
return highlights;
}
@Override
public long getNotificationCount() {
return global;
}
}
public class JoinedRoomJson extends MatrixJsonObject implements _SyncData.JoinedRoom {
private String id;
private State state;
private Timeline timeline;
private UnreadNotifications unreadNotifications;
private Ephemeral ephemeral;
private AccountData accountData;
public JoinedRoomJson(String id, JsonObject data) {
super(data);
this.id = id;
this.state = new StateJson(findObj("state").orElseGet(JsonObject::new));
this.timeline = new TimelineJson(findObj("timeline").orElseGet(JsonObject::new));
this.unreadNotifications = new UnreadNotificationsJson(computeObj("unread_notifications"));
this.ephemeral = new EphemeralJson(findObj("ephemeral").orElseGet(JsonObject::new));
this.accountData = new AccountDataJson(findObj("account_data").orElseGet(JsonObject::new));
}
@Override
public String getId() {
return id;
}
@Override
public State getState() {
return state;
}
@Override
public Timeline getTimeline() {
return timeline;
}
@Override
public Ephemeral getEphemeral() {
return ephemeral;
}
@Override
public AccountData getAccountData() {
return accountData;
}
@Override
public UnreadNotifications getUnreadNotifications() {
return unreadNotifications;
}
}
public class LeftRoomJson extends MatrixPersistentEventJson implements _SyncData.LeftRoom {
private String id;
private State state;
private Timeline timeline;
public LeftRoomJson(String id, JsonObject data) {
super(data);
this.id = id;
this.state = new StateJson(findObj("state").orElseGet(JsonObject::new));
this.timeline = new TimelineJson(findObj("timeline").orElseGet(JsonObject::new));
}
@Override
public String getId() {
return id;
}
@Override
public State getState() {
return state;
}
@Override
public Timeline getTimeline() {
return timeline;
}
}
public class RoomsJson extends MatrixJsonObject implements _SyncData.Rooms {
private Set<InvitedRoom> invited = new HashSet<>();
private Set<JoinedRoom> joined = new HashSet<>();
private Set<LeftRoom> left = new HashSet<>();
public RoomsJson(JsonObject obj) {
super(obj);
findObj("invite").ifPresent(o -> {
for (Map.Entry<String, JsonElement> entry : o.entrySet()) {
invited.add(new InvitedRoomJson(entry.getKey(), asObj(entry.getValue())));
}
});
findObj("join").ifPresent(o -> {
for (Map.Entry<String, JsonElement> entry : o.entrySet()) {
joined.add(new JoinedRoomJson(entry.getKey(), asObj(entry.getValue())));
}
});
findObj("leave").ifPresent(o -> {
for (Map.Entry<String, JsonElement> entry : o.entrySet()) {
left.add(new LeftRoomJson(entry.getKey(), asObj(entry.getValue())));
}
});
}
@Override
public Set<InvitedRoom> getInvited() {
return invited;
}
@Override
public Set<JoinedRoom> getJoined() {
return joined;
}
@Override
public Set<LeftRoom> getLeft() {
return left;
}
}
private String nextBatch;
private AccountDataJson accountData;
private RoomsJson rooms;
public SyncDataJson(JsonObject data) {
super(data);
nextBatch = getString("next_batch");
accountData = new AccountDataJson(findObj("account_data").orElseGet(JsonObject::new));
rooms = new RoomsJson(findObj("rooms").orElseGet(JsonObject::new));
}
@Override
public String nextBatchToken() {
return nextBatch;
}
@Override
public AccountData getAccountData() {
return accountData;
}
@Override
public Rooms getRooms() {
return rooms;
}
}

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