Compare commits
163 Commits
Author | SHA1 | Date | |
---|---|---|---|
56bfdda18c | |||
fb3debfb49 | |||
49f812f867 | |||
de0a3152c3 | |||
cc5d047c3f | |||
350776df17 | |||
7b6560e9c8 | |||
4fd4fdac60 | |||
dfad9d9ce8 | |||
d2fc4e3bef | |||
4416c17216 | |||
f54ed462b1 | |||
7498dcf122 | |||
7e5665a56f | |||
f633e9d256 | |||
bdfe4a00a9 | |||
50d4f0fa30 | |||
bb190230b2 | |||
c9bafd9af2 | |||
3c9c8ca1b5 | |||
dacf96140c | |||
1d228d75e6 | |||
62530eda08 | |||
15c3e6b425 | |||
945a2dbdf5 | |||
d5cebf103d | |||
de14f80a40 | |||
7ed6db537d | |||
9d8d88527c | |||
a1c82878f2 | |||
ab95c8ee32 | |||
e50a49b198 | |||
4492888557 | |||
ed5407fc42 | |||
a2911f2ace | |||
e99578d168 | |||
d471ea71d7 | |||
56115df282 | |||
2e1194d216 | |||
ee001d543c | |||
a826eb319e | |||
6b557e9954 | |||
041a4d1b73 | |||
c1a1741bfb | |||
6e1058c484 | |||
5f6fb38485 | |||
c9046ffbd0 | |||
cf97c5a88f | |||
3d1a8c4495 | |||
46dc07ef07 | |||
d9ab2b8739 | |||
14032aaf50 | |||
73051c3d00 | |||
a3c8c4a8cf | |||
453751a39e | |||
ad23e91ece | |||
baabdfc2ef | |||
263e3260c9 | |||
a281bdbf77 | |||
338e7f4aa2 | |||
a9212ce73a | |||
f2e1fcb25f | |||
f5630bc4d9 | |||
dbccaecfd6 | |||
de0842d3a8 | |||
05424f14b8 | |||
2587e6be7c | |||
20062fe5bc | |||
0ec6462ff4 | |||
567c7bb972 | |||
1c6322f1c2 | |||
603cdd1340 | |||
a6164379c5 | |||
fc9d6b3a36 | |||
8b14664dc7 | |||
f0922d67df | |||
6e05b51fdc | |||
0999ef6309 | |||
cae00f4606 | |||
941ae697e7 | |||
fbf1ea8f5b | |||
4eef3ac40d | |||
e6c90aa00f | |||
ca306a3212 | |||
e8864afe02 | |||
f09ede8780 | |||
98146d3468 | |||
5f64b72a91 | |||
acb2b5a37d | |||
9f9c48aaa7 | |||
eb6e541e15 | |||
e45ac5a78a | |||
aebcb7b5e1 | |||
c45f95ce3f | |||
640fa8e9f1 | |||
0f3c37bf6a | |||
|
ae5864cd91 | ||
|
e456724caf | ||
|
ed9dcc4061 | ||
|
ea8e386939 | ||
|
e0ec887118 | ||
|
a71d32ba77 | ||
|
a0f6fe9b0d | ||
|
c25647156a | ||
|
e7c4c12a98 | ||
|
90b2b5301c | ||
|
0d93a26e6d | ||
|
c29fc0f0eb | ||
|
e421c851c9 | ||
|
5b2b45233a | ||
|
888f7a4209 | ||
|
0c301a49c7 | ||
|
1fda2dd3b7 | ||
|
c4a20efe5e | ||
|
fc45f1b090 | ||
|
1480507d76 | ||
|
a1ab1e8e0a | ||
|
1e5033b461 | ||
|
7323851c6e | ||
|
08db73e55b | ||
|
9fba20475b | ||
|
9af5fce014 | ||
|
9843e14c1a | ||
|
60e6f1e23c | ||
|
6cdbcc69c7 | ||
|
ed7c714738 | ||
|
a9d783192b | ||
|
2bb5a734d1 | ||
|
9aa5c4cca9 | ||
|
9c4faab5d8 | ||
|
53c4ffdc4e | ||
|
e4144e923a | ||
|
791361c10d | ||
|
7c94bd4744 | ||
|
4b5eecd7e7 | ||
|
a6968fb7e9 | ||
|
d4853b1154 | ||
|
89df4b2425 | ||
|
0f89121b98 | ||
|
8a40ca185b | ||
|
5baeb42623 | ||
|
072e5f66cb | ||
|
b2f41d689b | ||
|
9b4aff58c7 | ||
|
a20e41574d | ||
|
72977d65ae | ||
|
7555fff1a5 | ||
|
aed12e5536 | ||
|
75efd9921d | ||
|
9219bd4723 | ||
|
73526be2ac | ||
|
b827efca2c | ||
|
6b7a4c8a23 | ||
|
47f6239268 | ||
|
0d6f65b469 | ||
|
be915aed94 | ||
|
ce938bb4a5 | ||
|
15db563e8d | ||
|
82a538c750 | ||
|
84ca8ebbd9 | ||
|
774ebf4fa8 | ||
|
eb1326c56a | ||
|
10cdb4360e |
71
.github/workflows/codeql-analysis.yml.disabled
vendored
Normal file
71
.github/workflows/codeql-analysis.yml.disabled
vendored
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
# For most projects, this workflow file will not need changing; you simply need
|
||||||
|
# to commit it to your repository.
|
||||||
|
#
|
||||||
|
# You may wish to alter this file to override the set of languages analyzed,
|
||||||
|
# or to provide custom queries or build logic.
|
||||||
|
name: "CodeQL"
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [master]
|
||||||
|
pull_request:
|
||||||
|
# The branches below must be a subset of the branches above
|
||||||
|
branches: [master]
|
||||||
|
schedule:
|
||||||
|
- cron: '0 3 * * 6'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
analyze:
|
||||||
|
name: Analyze
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
# Override automatic language detection by changing the below list
|
||||||
|
# Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python']
|
||||||
|
language: ['java']
|
||||||
|
# Learn more...
|
||||||
|
# https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
# We must fetch at least the immediate parents so that if this is
|
||||||
|
# a pull request then we can checkout the head.
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
# If this run was triggered by a pull request event, then checkout
|
||||||
|
# the head of the pull request instead of the merge commit.
|
||||||
|
- run: git checkout HEAD^2
|
||||||
|
if: ${{ github.event_name == 'pull_request' }}
|
||||||
|
|
||||||
|
# Initializes the CodeQL tools for scanning.
|
||||||
|
- name: Initialize CodeQL
|
||||||
|
uses: github/codeql-action/init@v2
|
||||||
|
with:
|
||||||
|
languages: ${{ matrix.language }}
|
||||||
|
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||||
|
# By default, queries listed here will override any specified in a config file.
|
||||||
|
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||||
|
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
||||||
|
|
||||||
|
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||||
|
# If this step fails, then you should remove it and run the build manually (see below)
|
||||||
|
- name: Autobuild
|
||||||
|
uses: github/codeql-action/autobuild@v2
|
||||||
|
|
||||||
|
# ℹ️ Command-line programs to run using the OS shell.
|
||||||
|
# 📚 https://git.io/JvXDl
|
||||||
|
|
||||||
|
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
||||||
|
# and modify them (or add more) to build your code if your project
|
||||||
|
# uses a compiled language
|
||||||
|
|
||||||
|
#- run: |
|
||||||
|
# make bootstrap
|
||||||
|
# make release
|
||||||
|
|
||||||
|
- name: Perform CodeQL Analysis
|
||||||
|
uses: github/codeql-action/analyze@v1
|
4
.gitignore
vendored
4
.gitignore
vendored
@@ -7,8 +7,8 @@ out/
|
|||||||
.idea/
|
.idea/
|
||||||
|
|
||||||
# Local dev config
|
# Local dev config
|
||||||
/ma1sd.yaml
|
/mxids.yaml
|
||||||
/application.yaml
|
/application.yaml
|
||||||
|
|
||||||
# Local dev storage
|
# Local dev storage
|
||||||
/ma1sd.db
|
/mxids.db
|
||||||
|
40
Dockerfile
40
Dockerfile
@@ -1,18 +1,36 @@
|
|||||||
FROM openjdk:8-jre-alpine
|
# Use a specific version of OpenJDK based on Debian ("bullseye" in this case)
|
||||||
|
FROM --platform=$BUILDPLATFORM openjdk:22-ea-21-jdk-slim-bullseye AS builder
|
||||||
|
|
||||||
RUN apk update && apk add bash && rm -rf /var/lib/apk/* /var/cache/apk/*
|
# Replace 'apk' commands with 'apt-get' for Debian-based package management.
|
||||||
|
# Install required packages such as 'git' and 'gradle'. Remember to update and clean up properly.
|
||||||
|
RUN apt-get update && \
|
||||||
|
apt-get install -y gradle git && \
|
||||||
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
VOLUME /etc/ma1sd
|
WORKDIR /mxids
|
||||||
VOLUME /var/ma1sd
|
COPY . .
|
||||||
|
RUN ./gradlew shadowJar
|
||||||
|
|
||||||
|
# Second stage: Setup the runtime container
|
||||||
|
FROM openjdk:22-ea-21-jdk-slim-bullseye
|
||||||
|
|
||||||
|
# Again, switch to 'apt-get' for installing 'bash'. Clean up to keep the image size down.
|
||||||
|
RUN apt-get update && \
|
||||||
|
apt-get install -y bash && \
|
||||||
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
VOLUME /etc/mxids
|
||||||
|
VOLUME /var/mxids
|
||||||
EXPOSE 8090
|
EXPOSE 8090
|
||||||
|
|
||||||
ENV JAVA_OPTS=""
|
ENV JAVA_OPTS=""
|
||||||
ENV CONF_FILE_PATH="/etc/ma1sd/ma1sd.yaml"
|
ENV CONF_FILE_PATH="/etc/mxids/mxids.yaml"
|
||||||
ENV SIGN_KEY_PATH="/var/ma1sd/sign.key"
|
ENV SIGN_KEY_PATH="/var/mxids/sign.key"
|
||||||
ENV SQLITE_DATABASE_PATH="/var/ma1sd/ma1sd.db"
|
ENV SQLITE_DATABASE_PATH="/var/mxids/mxids.db"
|
||||||
|
|
||||||
CMD [ "/start.sh" ]
|
# It's usually a good practice to use 'COPY' instead of 'ADD' for local files unless you need the extra capabilities of 'ADD' (like auto-extracting tar files).
|
||||||
|
COPY src/docker/start.sh /start.sh
|
||||||
|
COPY src/script/mxids /app/mxids
|
||||||
|
COPY --from=builder /mxids/build/libs/mxids.jar /app/mxids.jar
|
||||||
|
|
||||||
ADD src/docker/start.sh /start.sh
|
CMD ["/start.sh"]
|
||||||
ADD src/script/ma1sd /app/ma1sd
|
|
||||||
ADD build/libs/ma1sd.jar /app/ma1sd.jar
|
|
||||||
|
16
DockerfileX
Normal file
16
DockerfileX
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
FROM --platform=$BUILDPLATFORM openjdk:11.0.16-jre-slim
|
||||||
|
|
||||||
|
VOLUME /etc/mxids
|
||||||
|
VOLUME /var/mxids
|
||||||
|
EXPOSE 8090
|
||||||
|
|
||||||
|
ENV JAVA_OPTS=""
|
||||||
|
ENV CONF_FILE_PATH="/etc/mxids/mxids.yaml"
|
||||||
|
ENV SIGN_KEY_PATH="/var/mxids/sign.key"
|
||||||
|
ENV SQLITE_DATABASE_PATH="/var/mxids/mxids.db"
|
||||||
|
|
||||||
|
CMD [ "/start.sh" ]
|
||||||
|
|
||||||
|
ADD src/docker/start.sh /start.sh
|
||||||
|
ADD src/script/mxids /app/mxids
|
||||||
|
ADD build/libs/mxids.jar /app/mxids.jar
|
154
build.gradle
154
build.gradle
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* ma1sd - Matrix Identity Server Daemon
|
* mxids - Matrix Identity Server
|
||||||
* Copyright (C) 2017 Kamax Sarl
|
* Copyright (C) 2017 Kamax Sarl
|
||||||
*
|
*
|
||||||
* https://www.kamax.io/
|
* https://www.kamax.io/
|
||||||
@@ -20,22 +20,22 @@
|
|||||||
|
|
||||||
import java.util.regex.Pattern
|
import java.util.regex.Pattern
|
||||||
|
|
||||||
apply plugin: 'java'
|
apply plugin: 'java-library'
|
||||||
apply plugin: 'application'
|
apply plugin: 'application'
|
||||||
apply plugin: 'com.github.johnrengelman.shadow'
|
apply plugin: 'com.github.johnrengelman.shadow'
|
||||||
apply plugin: 'idea'
|
apply plugin: 'idea'
|
||||||
apply plugin: 'com.github.ben-manes.versions'
|
apply plugin: 'com.github.ben-manes.versions'
|
||||||
|
|
||||||
def confFileName = "ma1sd.example.yaml"
|
def confFileName = "mxids.example.yaml"
|
||||||
def distDir = "${project.buildDir}/dist"
|
def distDir = "${project.buildDir}/dist"
|
||||||
|
|
||||||
def debBinPath = "/usr/lib/ma1sd"
|
def debBinPath = "/usr/lib/mxids"
|
||||||
def debConfPath = "/etc/ma1sd"
|
def debConfPath = "/etc/mxids"
|
||||||
def debDataPath = "/var/lib/ma1sd"
|
def debDataPath = "/var/lib/mxids"
|
||||||
def debSystemdPath = "/etc/systemd/system"
|
def debSystemdPath = "/etc/systemd/system"
|
||||||
|
|
||||||
def debConfFileName = confFileName
|
def debConfFileName = confFileName
|
||||||
def debStartScriptFilename = "ma1sd"
|
def debStartScriptFilename = "mxids"
|
||||||
|
|
||||||
def debBuildBasePath = "${project.buildDir}/tmp/debian"
|
def debBuildBasePath = "${project.buildDir}/tmp/debian"
|
||||||
def debBuildDebianPath = "${debBuildBasePath}/DEBIAN"
|
def debBuildDebianPath = "${debBuildBasePath}/DEBIAN"
|
||||||
@@ -44,18 +44,18 @@ 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 = "cqrenet/mxids"
|
||||||
def dockerImageTag = "${dockerImageName}:${ma1sdVersion()}"
|
def dockerImageTag = "${dockerImageName}:${mxidsVersion()}"
|
||||||
|
|
||||||
group = 'io.kamax'
|
group = 'io.kamax'
|
||||||
mainClassName = 'io.kamax.mxisd.MxisdStandaloneExec'
|
mainClassName = 'io.kamax.mxisd.MxisdStandaloneExec'
|
||||||
sourceCompatibility = '1.8'
|
sourceCompatibility = '11'
|
||||||
targetCompatibility = '1.8'
|
targetCompatibility = '11'
|
||||||
|
|
||||||
String ma1sdVersion() {
|
String mxidsVersion() {
|
||||||
def versionPattern = Pattern.compile("v(\\d+\\.)?(\\d+\\.)?(\\d+)(-.*)?")
|
def versionPattern = Pattern.compile("v(\\d+\\.)?(\\d+\\.)?(\\d+)(-.*)?")
|
||||||
|
|
||||||
String version = System.getenv('MA1SD_BUILD_VERSION')
|
String version = System.getenv('MXIDS_BUILD_VERSION')
|
||||||
if (version == null || version.size() == 0) {
|
if (version == null || version.size() == 0) {
|
||||||
version = gitVersion()
|
version = gitVersion()
|
||||||
}
|
}
|
||||||
@@ -73,109 +73,114 @@ String gitVersion() {
|
|||||||
|
|
||||||
buildscript {
|
buildscript {
|
||||||
repositories {
|
repositories {
|
||||||
jcenter()
|
gradlePluginPortal()
|
||||||
|
mavenCentral()
|
||||||
|
google()
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.github.jengelman.gradle.plugins:shadow:5.1.0'
|
classpath 'com.github.johnrengelman:shadow:8.1.1'
|
||||||
classpath 'com.github.ben-manes:gradle-versions-plugin:0.27.0'
|
classpath 'com.github.ben-manes:gradle-versions-plugin:0.51.0'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
jcenter()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
// Logging
|
// Logging
|
||||||
compile 'org.slf4j:slf4j-simple:1.7.25'
|
api 'org.slf4j:slf4j-simple:2.0.12'
|
||||||
|
|
||||||
// Easy file management
|
// Easy file management
|
||||||
compile 'commons-io:commons-io:2.6'
|
api 'commons-io:commons-io:2.16.0'
|
||||||
|
|
||||||
// Config management
|
// Config management
|
||||||
compile 'org.yaml:snakeyaml:1.25'
|
api 'org.yaml:snakeyaml:1.33'
|
||||||
|
|
||||||
// Dependencies from old Matrix-java-sdk
|
// Dependencies from old Matrix-java-sdk
|
||||||
compile 'org.apache.commons:commons-lang3:3.9'
|
api 'org.apache.commons:commons-lang3:3.14.0'
|
||||||
compile 'com.squareup.okhttp3:okhttp:4.2.2'
|
api 'com.squareup.okhttp3:okhttp:4.12.0'
|
||||||
compile 'commons-codec:commons-codec:1.13'
|
api 'commons-codec:commons-codec:1.16.1'
|
||||||
|
|
||||||
// ORMLite
|
// ORMLite
|
||||||
compile 'com.j256.ormlite:ormlite-jdbc:5.1'
|
api 'com.j256.ormlite:ormlite-jdbc:6.1'
|
||||||
|
|
||||||
// ed25519 handling
|
// ed25519 handling
|
||||||
compile 'net.i2p.crypto:eddsa:0.3.0'
|
api 'net.i2p.crypto:eddsa:0.3.0'
|
||||||
|
|
||||||
// LDAP connector
|
// LDAP connector
|
||||||
compile 'org.apache.directory.api:api-all:1.0.0'
|
api 'org.apache.directory.api:api-all:2.1.6'
|
||||||
|
|
||||||
// DNS lookups
|
// DNS lookups
|
||||||
compile 'dnsjava:dnsjava:2.1.9'
|
api 'dnsjava:dnsjava:3.5.3'
|
||||||
|
|
||||||
// HTTP connections
|
// HTTP connections
|
||||||
compile 'org.apache.httpcomponents:httpclient:4.5.10'
|
api 'org.apache.httpcomponents:httpclient:4.5.14'
|
||||||
|
|
||||||
// Phone numbers validation
|
// Phone numbers validation
|
||||||
compile 'com.googlecode.libphonenumber:libphonenumber:8.10.22'
|
api 'com.googlecode.libphonenumber:libphonenumber:8.13.34'
|
||||||
|
|
||||||
// E-mail sending
|
// E-mail sending
|
||||||
compile 'javax.mail:javax.mail-api:1.6.2'
|
api 'javax.mail:javax.mail-api:1.6.2'
|
||||||
compile 'com.sun.mail:javax.mail:1.6.2'
|
api 'com.sun.mail:javax.mail:1.6.2'
|
||||||
|
|
||||||
// Google Firebase Authentication backend
|
// Google Firebase Authentication backend
|
||||||
compile 'com.google.firebase:firebase-admin:5.3.0'
|
api 'com.google.firebase:firebase-admin:9.2.0'
|
||||||
|
|
||||||
// Connection Pool
|
// Connection Pool
|
||||||
compile 'com.mchange:c3p0:0.9.5.4'
|
api 'com.mchange:c3p0:0.10.0'
|
||||||
|
|
||||||
// SQLite
|
// SQLite
|
||||||
compile 'org.xerial:sqlite-jdbc:3.28.0'
|
api 'org.xerial:sqlite-jdbc:3.45.2.0'
|
||||||
|
|
||||||
// PostgreSQL
|
// PostgreSQL
|
||||||
compile 'org.postgresql:postgresql:42.2.8'
|
api 'org.postgresql:postgresql:42.7.3'
|
||||||
|
|
||||||
// MariaDB/MySQL
|
// MariaDB/MySQL
|
||||||
compile 'org.mariadb.jdbc:mariadb-java-client:2.5.1'
|
api 'org.mariadb.jdbc:mariadb-java-client:3.3.3'
|
||||||
|
|
||||||
|
// UNIX sockets
|
||||||
|
api 'com.kohlschutter.junixsocket:junixsocket-core:2.9.0'
|
||||||
|
|
||||||
// Twilio SDK for SMS
|
// Twilio SDK for SMS
|
||||||
compile 'com.twilio.sdk:twilio:7.45.0'
|
api 'com.twilio.sdk:twilio:10.1.3'
|
||||||
|
|
||||||
// SendGrid SDK to send emails from GCE
|
// SendGrid SDK to send emails from GCE
|
||||||
compile 'com.sendgrid:sendgrid-java:2.2.2'
|
api 'com.sendgrid:sendgrid-java:4.10.2'
|
||||||
|
|
||||||
// ZT-Exec for exec identity store
|
// ZT-Exec for exec identity store
|
||||||
compile 'org.zeroturnaround:zt-exec:1.11'
|
api 'org.zeroturnaround:zt-exec:1.12'
|
||||||
|
|
||||||
// HTTP server
|
// HTTP server
|
||||||
compile 'io.undertow:undertow-core:2.0.27.Final'
|
api 'io.undertow:undertow-core:2.3.12.Final'
|
||||||
|
|
||||||
// Command parser for AS interface
|
// Command parser for AS interface
|
||||||
implementation 'commons-cli:commons-cli:1.4'
|
api 'commons-cli:commons-cli:1.6.0'
|
||||||
|
|
||||||
testCompile 'junit:junit:4.13-rc-1'
|
testImplementation 'junit:junit:4.13.2'
|
||||||
testCompile 'com.github.tomakehurst:wiremock:2.25.1'
|
testImplementation 'com.github.tomakehurst:wiremock:3.0.1'
|
||||||
testCompile 'com.unboundid:unboundid-ldapsdk:4.0.12'
|
testImplementation 'com.unboundid:unboundid-ldapsdk:7.0.0'
|
||||||
testCompile 'com.icegreen:greenmail:1.5.11'
|
testImplementation 'com.icegreen:greenmail:1.6.15'
|
||||||
}
|
}
|
||||||
|
|
||||||
jar {
|
jar {
|
||||||
manifest {
|
manifest {
|
||||||
attributes(
|
attributes(
|
||||||
'Implementation-Version': ma1sdVersion()
|
'Implementation-Version': mxidsVersion()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
shadowJar {
|
shadowJar {
|
||||||
baseName = project.name
|
archiveBaseName.set(project.name)
|
||||||
classifier = null
|
archiveClassifier.set('') // Set to an empty string if you don't need a classifier.
|
||||||
version = null
|
archiveVersion.set('') // Set to an empty string if you don't want the version in the jar name.
|
||||||
}
|
}
|
||||||
|
|
||||||
task debBuild(dependsOn: shadowJar) {
|
task debBuild(dependsOn: shadowJar) {
|
||||||
doLast {
|
doLast {
|
||||||
String debVersion = ma1sdVersion()
|
String debVersion = mxidsVersion()
|
||||||
println "Version for package: ${debVersion}"
|
println "Version for package: ${debVersion}"
|
||||||
mkdir distDir
|
mkdir distDir
|
||||||
mkdir debBuildBasePath
|
mkdir debBuildBasePath
|
||||||
@@ -186,7 +191,7 @@ task debBuild(dependsOn: shadowJar) {
|
|||||||
mkdir debBuildSystemdPath
|
mkdir debBuildSystemdPath
|
||||||
|
|
||||||
copy {
|
copy {
|
||||||
from "${project.buildDir}/libs/ma1sd.jar"
|
from "${project.buildDir}/libs/mxids.jar"
|
||||||
into debBuildBinPath
|
into debBuildBinPath
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -234,12 +239,12 @@ task debBuild(dependsOn: shadowJar) {
|
|||||||
ant.replace(
|
ant.replace(
|
||||||
file: "${debBuildDebianPath}/postinst",
|
file: "${debBuildDebianPath}/postinst",
|
||||||
token: '%DEB_CONF_FILE%',
|
token: '%DEB_CONF_FILE%',
|
||||||
value: "${debConfPath}/ma1sd.yaml"
|
value: "${debConfPath}/mxids.yaml"
|
||||||
)
|
)
|
||||||
|
|
||||||
ant.chmod(
|
ant.chmod(
|
||||||
file: "${debBuildDebianPath}/postinst",
|
file: "${debBuildDebianPath}/postinst",
|
||||||
perm: 'a+x'
|
perm: '0755'
|
||||||
)
|
)
|
||||||
|
|
||||||
ant.chmod(
|
ant.chmod(
|
||||||
@@ -248,7 +253,7 @@ task debBuild(dependsOn: shadowJar) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
copy {
|
copy {
|
||||||
from "${project.file('src/systemd/ma1sd.service')}"
|
from "${project.file('src/systemd/mxids.service')}"
|
||||||
into debBuildSystemdPath
|
into debBuildSystemdPath
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,7 +269,7 @@ task debBuild(dependsOn: shadowJar) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
task dockerBuild(type: Exec, dependsOn: shadowJar) {
|
task dockerBuild(type: Exec) {
|
||||||
commandLine 'docker', 'build', '-t', dockerImageTag, project.rootDir
|
commandLine 'docker', 'build', '-t', dockerImageTag, project.rootDir
|
||||||
|
|
||||||
doLast {
|
doLast {
|
||||||
@@ -274,6 +279,15 @@ task dockerBuild(type: Exec, dependsOn: shadowJar) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
task dockerBuildX(type: Exec, dependsOn: shadowJar) {
|
||||||
|
commandLine 'docker', 'buildx', 'build', '--push', '--platform', 'linux/arm64,linux/amd64,linux/arm/v7', '-t', dockerImageTag , project.rootDir
|
||||||
|
doLast {
|
||||||
|
exec {
|
||||||
|
commandLine 'docker', 'buildx', 'build', '--push', '--platform', 'linux/arm64,linux/amd64,linux/arm/v7', '-t', "${dockerImageName}:latest-dev", project.rootDir
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
task dockerPush(type: Exec) {
|
task dockerPush(type: Exec) {
|
||||||
commandLine 'docker', 'push', dockerImageTag
|
commandLine 'docker', 'push', dockerImageTag
|
||||||
|
|
||||||
@@ -283,3 +297,33 @@ task dockerPush(type: Exec) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
task dockerPushX(type: Exec) {
|
||||||
|
commandLine 'docker', 'push', dockerImageTag
|
||||||
|
|
||||||
|
doLast {
|
||||||
|
exec {
|
||||||
|
commandLine 'docker', 'push', "${dockerImageName}:latest-dev"
|
||||||
|
commandLine 'docker', 'push', "${dockerImageName}:latest-amd64-dev"
|
||||||
|
commandLine 'docker', 'push', "${dockerImageName}:latest-arm64-dev"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.named('assemble').configure {
|
||||||
|
dependsOn shadowJar
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.withType(AbstractArchiveTask) {
|
||||||
|
if (it.name == 'distZip' || it.name == 'distTar') {
|
||||||
|
dependsOn shadowJar
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.named('startScripts').configure {
|
||||||
|
mustRunAfter shadowJar
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.named('startShadowScripts').configure {
|
||||||
|
dependsOn jar
|
||||||
|
}
|
||||||
|
@@ -7,7 +7,7 @@ Default values:
|
|||||||
```.yaml
|
```.yaml
|
||||||
matrix:
|
matrix:
|
||||||
v1: true # deprecated
|
v1: true # deprecated
|
||||||
v2: true
|
v2: false
|
||||||
```
|
```
|
||||||
|
|
||||||
To disable change value to `false`.
|
To disable change value to `false`.
|
||||||
@@ -19,8 +19,14 @@ matrix:
|
|||||||
```
|
```
|
||||||
NOTE: Riot Web version 1.5.5 and below checks the v1 for backward compatibility.
|
NOTE: Riot Web version 1.5.5 and below checks the v1 for backward compatibility.
|
||||||
|
|
||||||
|
NOTE: v2 disabled by default in order to preserve backward compatibility.
|
||||||
|
|
||||||
## Terms
|
## Terms
|
||||||
|
|
||||||
|
###### Requires: No.
|
||||||
|
|
||||||
|
Administrator can omit terms configuration. In this case the terms checking will be disabled.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
```.yaml
|
```.yaml
|
||||||
policy:
|
policy:
|
||||||
@@ -45,7 +51,7 @@ Where:
|
|||||||
- `version` -- the terms version.
|
- `version` -- the terms version.
|
||||||
- `lang` -- the term language.
|
- `lang` -- the term language.
|
||||||
- `name` -- the name of the term.
|
- `name` -- the name of the term.
|
||||||
- `url` -- the url of the term.
|
- `url` -- the url of the term. Might be any url (i.e. from another host) for a html page.
|
||||||
- `regexp` -- regexp patterns for API which should be available only after accepting the terms.
|
- `regexp` -- regexp patterns for API which should be available only after accepting the terms.
|
||||||
|
|
||||||
API will be checks for accepted terms only with authorization.
|
API will be checks for accepted terms only with authorization.
|
||||||
@@ -72,6 +78,10 @@ There is only one exception: [`POST /_matrix/identity/v2/terms`](https://matrix.
|
|||||||
|
|
||||||
Hashes and the pepper updates together according to the `rotationPolicy`.
|
Hashes and the pepper updates together according to the `rotationPolicy`.
|
||||||
|
|
||||||
|
###### Requires: No.
|
||||||
|
|
||||||
|
In case the `none` algorithms ma1sd will be lookup using the v1 bulk API.
|
||||||
|
|
||||||
```.yaml
|
```.yaml
|
||||||
hashing:
|
hashing:
|
||||||
enabled: true # enable or disable the hash lookup MSC2140 (default is false)
|
enabled: true # enable or disable the hash lookup MSC2140 (default is false)
|
||||||
@@ -100,7 +110,7 @@ For sql provider (i.e. for the `synapseSql`):
|
|||||||
```.yaml
|
```.yaml
|
||||||
synapseSql:
|
synapseSql:
|
||||||
lookup:
|
lookup:
|
||||||
query: 'select user_id as mxid, medium, address from user_threepids' # query for retrive 3PIDs for hashes.
|
query: 'select user_id as mxid, medium, address from user_threepid_id_server' # query for retrive 3PIDs for hashes.
|
||||||
```
|
```
|
||||||
|
|
||||||
For general sql provider:
|
For general sql provider:
|
||||||
@@ -126,5 +136,11 @@ exec:
|
|||||||
hashEnabled: true # enable the hash lookup (defaults is false)
|
hashEnabled: true # enable the hash lookup (defaults is false)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
For ldap providers:
|
||||||
|
```.yaml
|
||||||
|
ldap:
|
||||||
|
lookup: true
|
||||||
|
```
|
||||||
|
|
||||||
NOTE: Federation requests work only with `none` algorithms.
|
NOTE: Federation requests work only with `none` algorithms.
|
||||||
|
|
||||||
|
@@ -16,7 +16,7 @@ TCP 443
|
|||||||
+<---------------------------------<+
|
+<---------------------------------<+
|
||||||
|
|
|
|
||||||
| +-------------------+
|
| +-------------------+
|
||||||
TCP 8090 +-> | ma1sd |
|
TCP 8090 +-> | mxids |
|
||||||
| |
|
| |
|
||||||
| - Profile's 3PIDs |
|
| - Profile's 3PIDs |
|
||||||
| - 3PID Invites |
|
| - 3PID Invites |
|
||||||
|
@@ -1,14 +1,18 @@
|
|||||||
# From source
|
# From source
|
||||||
- [Binaries](#binaries)
|
- [From source](#from-source)
|
||||||
- [Requirements](#requirements)
|
- [Binaries](#binaries)
|
||||||
- [Build](#build)
|
- [Requirements](#requirements)
|
||||||
- [Debian package](#debian-package)
|
- [Build](#build)
|
||||||
- [Docker image](#docker-image)
|
- [Debian package](#debian-package)
|
||||||
- [Next steps](#next-steps)
|
- [Docker image](#docker-image)
|
||||||
|
- [Multi-platform builds](#multi-platform-builds)
|
||||||
|
- [Next steps](#next-steps)
|
||||||
|
|
||||||
## Binaries
|
## Binaries
|
||||||
### Requirements
|
### Requirements
|
||||||
- JDK 1.8
|
- JDK 1.8
|
||||||
|
- OpenJDK 11
|
||||||
|
- OpenJDK 14
|
||||||
|
|
||||||
### Build
|
### Build
|
||||||
```bash
|
```bash
|
||||||
@@ -17,7 +21,7 @@ cd ma1sd
|
|||||||
./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 `mxids.example.yaml` to `mxids.yaml` and edit to your needs.
|
||||||
For advanced configuration, see the [Configure section](configure.md).
|
For advanced configuration, see the [Configure section](configure.md).
|
||||||
|
|
||||||
Start the server in foreground to validate the build and configuration:
|
Start the server in foreground to validate the build and configuration:
|
||||||
@@ -70,5 +74,13 @@ Then follow the instruction in the [Debian package](install/debian.md) document.
|
|||||||
```
|
```
|
||||||
Then follow the instructions in the [Docker install](install/docker.md#configure) document.
|
Then follow the instructions in the [Docker install](install/docker.md#configure) document.
|
||||||
|
|
||||||
|
### Multi-platform builds
|
||||||
|
|
||||||
|
Provided with experimental docker feature [buildx](https://docs.docker.com/buildx/working-with-buildx/)
|
||||||
|
To build the arm64 and amd64 images run:
|
||||||
|
```bash
|
||||||
|
./gradlew dockerBuildX
|
||||||
|
```
|
||||||
|
|
||||||
## Next steps
|
## Next steps
|
||||||
- [Integrate with your infrastructure](getting-started.md#integrate)
|
- [Integrate with your infrastructure](getting-started.md#integrate)
|
||||||
|
@@ -58,7 +58,53 @@ Commonly the `server.publicUrl` should be the same value as the `trusted_third_p
|
|||||||
|
|
||||||
## Storage
|
## Storage
|
||||||
### SQLite
|
### SQLite
|
||||||
`storage.provider.sqlite.database`: Absolute location of the SQLite database
|
```yaml
|
||||||
|
storage:
|
||||||
|
backend: sqlite # default
|
||||||
|
provider:
|
||||||
|
sqlite:
|
||||||
|
database: /var/lib/ma1sd/store.db # Absolute location of the SQLite database
|
||||||
|
```
|
||||||
|
|
||||||
|
### Postgresql
|
||||||
|
```yaml
|
||||||
|
storage:
|
||||||
|
backend: postgresql
|
||||||
|
provider:
|
||||||
|
postgresql:
|
||||||
|
database: //localhost:5432/ma1sd
|
||||||
|
username: ma1sd
|
||||||
|
password: secret_password
|
||||||
|
```
|
||||||
|
See [the migration instruction](migration-to-postgresql.md) from sqlite to postgresql
|
||||||
|
|
||||||
|
|
||||||
|
## Logging
|
||||||
|
```yaml
|
||||||
|
logging:
|
||||||
|
root: error # default level for all loggers (apps and thirdparty libraries)
|
||||||
|
app: info # log level only for the ma1sd
|
||||||
|
requests: false # log request and response
|
||||||
|
```
|
||||||
|
|
||||||
|
Possible value: `trace`, `debug`, `info`, `warn`, `error`, `off`.
|
||||||
|
|
||||||
|
Default value for root level: `info`.
|
||||||
|
|
||||||
|
Value for app level can be specified via `MA1SD_LOG_LEVEL` environment variable, configuration or start options.
|
||||||
|
|
||||||
|
Default value for app level: `info`.
|
||||||
|
|
||||||
|
| start option | equivalent configuration |
|
||||||
|
| --- | --- |
|
||||||
|
| | app: info |
|
||||||
|
| -v | app: debug |
|
||||||
|
| -vv | app: trace |
|
||||||
|
|
||||||
|
#### WARNING
|
||||||
|
|
||||||
|
The setting `logging.requests` *MUST NOT* be used in production due it prints full unmasked request and response into the log and can be cause of the data leak.
|
||||||
|
This setting can be used only to testing and debugging errors.
|
||||||
|
|
||||||
## Identity stores
|
## Identity stores
|
||||||
See the [Identity stores](stores/README.md) for specific configuration
|
See the [Identity stores](stores/README.md) for specific configuration
|
||||||
|
@@ -56,8 +56,7 @@ 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 ma1sd 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 ma1sd?
|
||||||
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) only handles one specific flow: validate credentials at login.
|
||||||
saying so and only handles on specific flow: validate credentials at login.
|
|
||||||
|
|
||||||
It does not:
|
It does not:
|
||||||
- Auto-provision user profiles
|
- Auto-provision user profiles
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
# Identity
|
# Identity
|
||||||
Implementation of the [Identity Service API r0.2.0](https://matrix.org/docs/spec/identity_service/r0.2.0.html).
|
Implementation of the [Identity Service API r0.3.0](https://matrix.org/docs/spec/identity_service/r0.3.0.html).
|
||||||
|
|
||||||
- [Lookups](#lookups)
|
- [Lookups](#lookups)
|
||||||
- [Invitations](#invitations)
|
- [Invitations](#invitations)
|
||||||
|
@@ -121,15 +121,13 @@ server {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Synapse
|
### Synapse (Deprecated with synapse v1.4.0)
|
||||||
Add your ma1sd domain into the `homeserver.yaml` at `trusted_third_party_id_servers` and restart synapse.
|
Add your ma1sd domain into the `homeserver.yaml` at `trusted_third_party_id_servers` and restart synapse.
|
||||||
In a typical configuration, you would end up with something similar to:
|
In a typical configuration, you would end up with something similar to:
|
||||||
```yaml
|
```yaml
|
||||||
trusted_third_party_id_servers:
|
trusted_third_party_id_servers:
|
||||||
- matrix.example.org
|
- matrix.example.org
|
||||||
```
|
```
|
||||||
It is **highly recommended** to remove `matrix.org` and `vector.im` (or any other default entry) from your configuration
|
|
||||||
so only your own Identity server is authoritative for your HS.
|
|
||||||
|
|
||||||
## Validate (Under reconstruction)
|
## Validate (Under reconstruction)
|
||||||
**NOTE:** In case your homeserver has no working federation, step 5 will not happen. If step 4 took place, consider
|
**NOTE:** In case your homeserver has no working federation, step 5 will not happen. If step 4 took place, consider
|
||||||
|
41
docs/migration-to-postgresql.md
Normal file
41
docs/migration-to-postgresql.md
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
# Migration from sqlite to postgresql
|
||||||
|
|
||||||
|
Starting from the version 2.3.0 ma1sd support postgresql for internal storage in addition to sqlite (parameters `storage.backend`).
|
||||||
|
|
||||||
|
#### Migration steps
|
||||||
|
|
||||||
|
1. create the postgresql database and user for ma1sd storage
|
||||||
|
2. create a backup for sqlite storage (default location: /var/lib/ma1sd/store.db)
|
||||||
|
3. migrate data from sqlite to postgresql
|
||||||
|
4. change ma1sd configuration to use the postgresql
|
||||||
|
|
||||||
|
For data migration is it possible to use https://pgloader.io tool.
|
||||||
|
|
||||||
|
Example of the migration command:
|
||||||
|
```shell script
|
||||||
|
pgloader --with "quote identifiers" /path/to/store.db pgsql://ma1sd_user:ma1sd_password@host:port/database
|
||||||
|
```
|
||||||
|
or (short version for database on localhost)
|
||||||
|
```shell script
|
||||||
|
pgloader --with "quote identifiers" /path/to/store.db pgsql://ma1sd_user:ma1sd_password@localhost/ma1sd
|
||||||
|
```
|
||||||
|
|
||||||
|
An option `--with "quote identifies"` used to create case sensitive tables.
|
||||||
|
ma1sd_user - postgresql user for ma1sd.
|
||||||
|
ma1sd_password - password of the postgresql user.
|
||||||
|
host - postgresql host
|
||||||
|
post - database port (default 5432)
|
||||||
|
database - database name.
|
||||||
|
|
||||||
|
|
||||||
|
Configuration example for postgresql storage:
|
||||||
|
```yaml
|
||||||
|
storage:
|
||||||
|
backend: postgresql
|
||||||
|
provider:
|
||||||
|
postgresql:
|
||||||
|
database: '//localhost/ma1sd' # or full variant //192.168.1.100:5432/ma1sd_database
|
||||||
|
username: 'ma1sd_user'
|
||||||
|
password: 'ma1sd_password'
|
||||||
|
```
|
||||||
|
|
@@ -1,7 +1,10 @@
|
|||||||
# Operations Guide
|
# Operations Guide
|
||||||
- [Overview](#overview)
|
- [Operations Guide](#operations-guide)
|
||||||
- [Maintenance](#maintenance)
|
- [Overview](#overview)
|
||||||
- [Backuo](#backup)
|
- [Maintenance](#maintenance)
|
||||||
|
- [Backup](#backup)
|
||||||
|
- [Run](#run)
|
||||||
|
- [Restore](#restore)
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
This document gives various information for the day-to-day management and operations of ma1sd.
|
This document gives various information for the day-to-day management and operations of ma1sd.
|
||||||
|
@@ -89,7 +89,7 @@ ldap:
|
|||||||
#### 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/java/io/kamax/mxisd/config/ldap/LdapConfig.java#L64)
|
||||||
for emails and phone number:
|
for emails and phone number:
|
||||||
```yaml
|
```yaml
|
||||||
ldap:
|
ldap:
|
||||||
|
@@ -136,7 +136,7 @@ sql:
|
|||||||
|
|
||||||
```
|
```
|
||||||
For the `role` query, `type` can be used to tell ma1sd how to inject the User ID in the query:
|
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.
|
- `uid` will extract and set only the localpart.
|
||||||
- `mxid` will use the ID as-is.
|
- `mxid` will use the ID as-is.
|
||||||
|
|
||||||
On each query, the first parameter `?` is set as a string with the corresponding ID format.
|
On each query, the first parameter `?` is set as a string with the corresponding ID format.
|
||||||
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,5 +1,5 @@
|
|||||||
#Thu Dec 05 22:39:36 MSK 2019
|
#Thu Dec 05 22:39:36 MSK 2019
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.0-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
@@ -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'
|
|
||||||
|
|
219
mxids.example.yaml
Normal file
219
mxids.example.yaml
Normal file
@@ -0,0 +1,219 @@
|
|||||||
|
# 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: false # deprecated
|
||||||
|
v2: true # MSC2140 API v2. Riot require enabled V2 API.
|
||||||
|
|
||||||
|
|
||||||
|
################
|
||||||
|
# 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/mxids/keys is a possible value
|
||||||
|
# For production, recommended location shall be one of the following:
|
||||||
|
# - /var/lib/mxids/keys
|
||||||
|
# - /var/opt/mxids/keys
|
||||||
|
# - /var/local/mxids/keys
|
||||||
|
#
|
||||||
|
key:
|
||||||
|
path: ''
|
||||||
|
|
||||||
|
|
||||||
|
# Path to the SQLite DB file for ma1sd internal storage
|
||||||
|
# /!\ THIS MUST **NOT** BE YOUR HOMESERVER DATABASE /!\
|
||||||
|
#
|
||||||
|
# Examples:
|
||||||
|
# - /var/opt/mxids/store.db
|
||||||
|
# - /var/local/mxids/store.db
|
||||||
|
# - /var/lib/mxids/store.db
|
||||||
|
#
|
||||||
|
storage:
|
||||||
|
# backend: sqlite # or postgresql
|
||||||
|
provider:
|
||||||
|
sqlite:
|
||||||
|
database: '/path/to/mxids.db'
|
||||||
|
# postgresql:
|
||||||
|
# # Wrap all string values with quotes to avoid yaml parsing mistakes
|
||||||
|
# database: '//localhost/mxids' # or full variant //192.168.1.100:5432/mxids_database
|
||||||
|
# username: 'mxids_user'
|
||||||
|
# password: 'mxids_password'
|
||||||
|
#
|
||||||
|
# # Pool configuration for postgresql backend.
|
||||||
|
# #######
|
||||||
|
# # Enable or disable pooling
|
||||||
|
# pool: false
|
||||||
|
#
|
||||||
|
# #######
|
||||||
|
# # Check database connection before get from pool
|
||||||
|
# testBeforeGetFromPool: false # or true
|
||||||
|
#
|
||||||
|
# #######
|
||||||
|
# # There is an internal thread which checks each of the database connections as a keep-alive mechanism. This set the
|
||||||
|
# # number of milliseconds it sleeps between checks -- default is 30000. To disable the checking thread, set this to
|
||||||
|
# # 0 before you start using the connection source.
|
||||||
|
# checkConnectionsEveryMillis: 30000
|
||||||
|
#
|
||||||
|
# #######
|
||||||
|
# # Set the number of connections that can be unused in the available list.
|
||||||
|
# maxConnectionsFree: 5
|
||||||
|
#
|
||||||
|
# #######
|
||||||
|
# # Set the number of milliseconds that a connection can stay open before being closed. Set to 9223372036854775807 to have
|
||||||
|
# # the connections never expire.
|
||||||
|
# maxConnectionAgeMillis: 3600000
|
||||||
|
|
||||||
|
###################
|
||||||
|
# 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_threepid_id_server' # query for retrive 3PIDs for hashes.
|
||||||
|
# legacyRoomNames: false # use the old query to get room names.
|
||||||
|
|
||||||
|
### hash lookup for ldap provider (with example of the ldap configuration)
|
||||||
|
# ldap:
|
||||||
|
# enabled: true
|
||||||
|
# lookup: true # hash lookup
|
||||||
|
# activeDirectory: false
|
||||||
|
# defaultDomain: ''
|
||||||
|
# connection:
|
||||||
|
# host: 'ldap.domain.tld'
|
||||||
|
# port: 389
|
||||||
|
# bindDn: 'cn=admin,dc=domain,dc=tld'
|
||||||
|
# bindPassword: 'Secret'
|
||||||
|
# baseDNs:
|
||||||
|
# - 'dc=domain,dc=tld'
|
||||||
|
# attribute:
|
||||||
|
# uid:
|
||||||
|
# type: 'uid' # or mxid
|
||||||
|
# value: 'cn'
|
||||||
|
# name: 'displayName'
|
||||||
|
# identity:
|
||||||
|
# filter: '(objectClass=inetOrgPerson)'
|
||||||
|
|
||||||
|
#### MSC2140 (Terms)
|
||||||
|
#policy:
|
||||||
|
# policies:
|
||||||
|
# term_name: # term name
|
||||||
|
# version: 1.0 # version
|
||||||
|
# terms:
|
||||||
|
# en: # lang
|
||||||
|
# name: term name en # localized name
|
||||||
|
# url: https://mxids.host.tld/term_en.html # localized url
|
||||||
|
# fe: # lang
|
||||||
|
# name: term name fr # localized name
|
||||||
|
# url: https://mxids.host.tld/term_fr.html # localized url
|
||||||
|
# regexp:
|
||||||
|
# - '/_matrix/identity/v2/account.*'
|
||||||
|
# - '/_matrix/identity/v2/hash_details'
|
||||||
|
# - '/_matrix/identity/v2/lookup'
|
||||||
|
#
|
||||||
|
|
||||||
|
# logging:
|
||||||
|
# root: error # default level for all loggers (apps and thirdparty libraries)
|
||||||
|
# app: info # log level only for the ma1sd
|
||||||
|
# requests: false # or true to dump full requests and responses
|
||||||
|
|
||||||
|
|
||||||
|
# Config invitation manager
|
||||||
|
#invite:
|
||||||
|
# fullDisplayName: true # print full name of the invited user (default false)
|
||||||
|
# resolution:
|
||||||
|
# timer: 10
|
||||||
|
# period: seconds # search invites every 10 seconds (by default 5 minutes)
|
||||||
|
|
||||||
|
|
||||||
|
# Internal API
|
||||||
|
#internal:
|
||||||
|
# enabled: true # default to false
|
11
renovate.json
Normal file
11
renovate.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"extends": [
|
||||||
|
"config:base"
|
||||||
|
],
|
||||||
|
"packageRules": [
|
||||||
|
{
|
||||||
|
"updateTypes": ["minor", "patch", "pin", "digest"],
|
||||||
|
"automerge": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@@ -1,6 +1,6 @@
|
|||||||
Package: ma1sd
|
Package: mxids
|
||||||
Maintainer: ma1uta <sablintolya@gmail.com>
|
Maintainer: ma1uta <sablintolya@gmail.com>
|
||||||
Homepage: https://github.com/ma1uta/ma1sd
|
Homepage: https://git.cqre.net/cqrenet/mxids.git
|
||||||
Description: Federated Matrix Identity Server
|
Description: Federated Matrix Identity Server
|
||||||
Architecture: all
|
Architecture: all
|
||||||
Section: net
|
Section: net
|
||||||
|
@@ -1,19 +1,19 @@
|
|||||||
#!/bin/bash -e
|
#!/bin/bash -e
|
||||||
|
|
||||||
# Add service account
|
# Add service account
|
||||||
useradd -r ma1sd || true
|
useradd -r mxids || true
|
||||||
|
|
||||||
# Set permissions for data directory
|
# Set permissions for data directory
|
||||||
chown -R ma1sd:ma1sd %DEB_DATA_DIR%
|
chown -R mxids:mxids %DEB_DATA_DIR%
|
||||||
|
|
||||||
# Create symlink to ma1sd run script
|
# Create symlink to mxids run script
|
||||||
ln -sfT /usr/lib/ma1sd/ma1sd /usr/bin/ma1sd
|
ln -sfT /usr/lib/mxids/mxids /usr/bin/mxids
|
||||||
|
|
||||||
# Enable systemd service
|
# Enable systemd service
|
||||||
systemctl enable ma1sd.service
|
systemctl enable mxids.service
|
||||||
|
|
||||||
# If we already have a config file setup, we attempt to run ma1sd automatically
|
# If we already have a config file setup, we attempt to run mxids automatically
|
||||||
# Specifically targeted at upgrades where the service needs to be restarted
|
# Specifically targeted at upgrades where the service needs to be restarted
|
||||||
if [ -f "%DEB_CONF_FILE%" ]; then
|
if [ -f "%DEB_CONF_FILE%" ]; then
|
||||||
systemctl restart ma1sd.service
|
systemctl restart mxids.service
|
||||||
fi
|
fi
|
||||||
|
@@ -1,10 +1,10 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# Stop running instance if needed
|
# Stop running instance if needed
|
||||||
systemctl stop ma1sd.service
|
systemctl stop mxids.service
|
||||||
|
|
||||||
# Disable service if exists
|
# Disable service if exists
|
||||||
systemctl disable ma1sd.service
|
systemctl disable mxids.service
|
||||||
|
|
||||||
# remove symlink
|
# remove symlink
|
||||||
rm /usr/bin/ma1sd
|
rm /usr/bin/mxids
|
||||||
|
@@ -27,8 +27,8 @@ if [[ -n "$CONF_FILE_PATH" ]] && [ ! -f "$CONF_FILE_PATH" ]; then
|
|||||||
echo >> "$CONF_FILE_PATH"
|
echo >> "$CONF_FILE_PATH"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Starting ma1sd..."
|
echo "Starting mxids..."
|
||||||
echo
|
echo
|
||||||
fi
|
fi
|
||||||
|
|
||||||
exec java -jar /app/ma1sd.jar -c /etc/ma1sd/ma1sd.yaml
|
exec java -jar /app/mxids.jar -c /etc/mxids/mxids.yaml
|
||||||
|
@@ -53,7 +53,7 @@ public class MatrixPath {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static MatrixPath clientR0() {
|
public static MatrixPath clientR0() {
|
||||||
return client().add("r0");
|
return client().add("v3");
|
||||||
}
|
}
|
||||||
|
|
||||||
private StringBuilder path = new StringBuilder();
|
private StringBuilder path = new StringBuilder();
|
||||||
|
@@ -27,7 +27,7 @@ public class ThreePid implements _ThreePid {
|
|||||||
|
|
||||||
public ThreePid(String medium, String address) {
|
public ThreePid(String medium, String address) {
|
||||||
this.medium = medium;
|
this.medium = medium;
|
||||||
this.address = address;
|
this.address = address.toLowerCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@@ -375,13 +375,13 @@ public abstract class AMatrixHttpClient implements _MatrixClientRaw {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected HttpUrl.Builder getClientPathBuilder(String... segments) {
|
protected HttpUrl.Builder getClientPathBuilder(String... segments) {
|
||||||
String[] base = { "client", "r0" };
|
String[] base = { "client", "v3" };
|
||||||
segments = ArrayUtils.addAll(base, segments);
|
segments = ArrayUtils.addAll(base, segments);
|
||||||
return getPathBuilder(segments);
|
return getPathBuilder(segments);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected HttpUrl.Builder getMediaPathBuilder(String... segments) {
|
protected HttpUrl.Builder getMediaPathBuilder(String... segments) {
|
||||||
String[] base = { "media", "r0" };
|
String[] base = { "media", "v3" };
|
||||||
segments = ArrayUtils.addAll(base, segments);
|
segments = ArrayUtils.addAll(base, segments);
|
||||||
return getPathBuilder(segments);
|
return getPathBuilder(segments);
|
||||||
}
|
}
|
||||||
|
@@ -23,11 +23,13 @@ package io.kamax.mxisd;
|
|||||||
import io.kamax.mxisd.config.MatrixConfig;
|
import io.kamax.mxisd.config.MatrixConfig;
|
||||||
import io.kamax.mxisd.config.MxisdConfig;
|
import io.kamax.mxisd.config.MxisdConfig;
|
||||||
import io.kamax.mxisd.config.PolicyConfig;
|
import io.kamax.mxisd.config.PolicyConfig;
|
||||||
|
import io.kamax.mxisd.config.ServerConfig;
|
||||||
import io.kamax.mxisd.http.undertow.handler.ApiHandler;
|
import io.kamax.mxisd.http.undertow.handler.ApiHandler;
|
||||||
import io.kamax.mxisd.http.undertow.handler.AuthorizationHandler;
|
import io.kamax.mxisd.http.undertow.handler.AuthorizationHandler;
|
||||||
import io.kamax.mxisd.http.undertow.handler.CheckTermsHandler;
|
import io.kamax.mxisd.http.undertow.handler.CheckTermsHandler;
|
||||||
import io.kamax.mxisd.http.undertow.handler.InternalInfoHandler;
|
import io.kamax.mxisd.http.undertow.handler.InternalInfoHandler;
|
||||||
import io.kamax.mxisd.http.undertow.handler.OptionsHandler;
|
import io.kamax.mxisd.http.undertow.handler.OptionsHandler;
|
||||||
|
import io.kamax.mxisd.http.undertow.handler.RequestDumpingHandler;
|
||||||
import io.kamax.mxisd.http.undertow.handler.SaneHandler;
|
import io.kamax.mxisd.http.undertow.handler.SaneHandler;
|
||||||
import io.kamax.mxisd.http.undertow.handler.as.v1.AsNotFoundHandler;
|
import io.kamax.mxisd.http.undertow.handler.as.v1.AsNotFoundHandler;
|
||||||
import io.kamax.mxisd.http.undertow.handler.as.v1.AsTransactionHandler;
|
import io.kamax.mxisd.http.undertow.handler.as.v1.AsTransactionHandler;
|
||||||
@@ -56,6 +58,7 @@ import io.kamax.mxisd.http.undertow.handler.identity.v1.BulkLookupHandler;
|
|||||||
import io.kamax.mxisd.http.undertow.handler.identity.v1.SingleLookupHandler;
|
import io.kamax.mxisd.http.undertow.handler.identity.v1.SingleLookupHandler;
|
||||||
import io.kamax.mxisd.http.undertow.handler.identity.v2.HashDetailsHandler;
|
import io.kamax.mxisd.http.undertow.handler.identity.v2.HashDetailsHandler;
|
||||||
import io.kamax.mxisd.http.undertow.handler.identity.v2.HashLookupHandler;
|
import io.kamax.mxisd.http.undertow.handler.identity.v2.HashLookupHandler;
|
||||||
|
import io.kamax.mxisd.http.undertow.handler.internal.InternalInviteManagerHandler;
|
||||||
import io.kamax.mxisd.http.undertow.handler.invite.v1.RoomInviteHandler;
|
import io.kamax.mxisd.http.undertow.handler.invite.v1.RoomInviteHandler;
|
||||||
import io.kamax.mxisd.http.undertow.handler.profile.v1.InternalProfileHandler;
|
import io.kamax.mxisd.http.undertow.handler.profile.v1.InternalProfileHandler;
|
||||||
import io.kamax.mxisd.http.undertow.handler.profile.v1.ProfileHandler;
|
import io.kamax.mxisd.http.undertow.handler.profile.v1.ProfileHandler;
|
||||||
@@ -99,35 +102,35 @@ public class HttpMxisd {
|
|||||||
public void start() {
|
public void start() {
|
||||||
m.start();
|
m.start();
|
||||||
|
|
||||||
HttpHandler asUserHandler = SaneHandler.around(new AsUserHandler(m.getAs()));
|
HttpHandler asUserHandler = sane(new AsUserHandler(m.getAs()));
|
||||||
HttpHandler asTxnHandler = SaneHandler.around(new AsTransactionHandler(m.getAs()));
|
HttpHandler asTxnHandler = sane(new AsTransactionHandler(m.getAs()));
|
||||||
HttpHandler asNotFoundHandler = SaneHandler.around(new AsNotFoundHandler(m.getAs()));
|
HttpHandler asNotFoundHandler = sane(new AsNotFoundHandler(m.getAs()));
|
||||||
|
|
||||||
final RoutingHandler handler = Handlers.routing()
|
final RoutingHandler handler = Handlers.routing()
|
||||||
.add("OPTIONS", "/**", SaneHandler.around(new OptionsHandler()))
|
.add("OPTIONS", "/**", sane(new OptionsHandler()))
|
||||||
|
|
||||||
// Status endpoints
|
// Status endpoints
|
||||||
.get(StatusHandler.Path, SaneHandler.around(new StatusHandler()))
|
.get(StatusHandler.Path, sane(new StatusHandler()))
|
||||||
.get(VersionHandler.Path, SaneHandler.around(new VersionHandler()))
|
.get(VersionHandler.Path, sane(new VersionHandler()))
|
||||||
|
|
||||||
// Authentication endpoints
|
// Authentication endpoints
|
||||||
.get(LoginHandler.Path, SaneHandler.around(new LoginGetHandler(m.getAuth(), m.getHttpClient())))
|
.get(LoginHandler.Path, sane(new LoginGetHandler(m.getAuth(), m.getHttpClient())))
|
||||||
.post(LoginHandler.Path, SaneHandler.around(new LoginPostHandler(m.getAuth())))
|
.post(LoginHandler.Path, sane(new LoginPostHandler(m.getAuth())))
|
||||||
.post(RestAuthHandler.Path, SaneHandler.around(new RestAuthHandler(m.getAuth())))
|
.post(RestAuthHandler.Path, sane(new RestAuthHandler(m.getAuth())))
|
||||||
|
|
||||||
// Directory endpoints
|
// Directory endpoints
|
||||||
.post(UserDirectorySearchHandler.Path, SaneHandler.around(new UserDirectorySearchHandler(m.getDirectory())))
|
.post(UserDirectorySearchHandler.Path, sane(new UserDirectorySearchHandler(m.getDirectory())))
|
||||||
|
|
||||||
// Profile endpoints
|
// Profile endpoints
|
||||||
.get(ProfileHandler.Path, SaneHandler.around(new ProfileHandler(m.getProfile())))
|
.get(ProfileHandler.Path, sane(new ProfileHandler(m.getProfile())))
|
||||||
.get(InternalProfileHandler.Path, SaneHandler.around(new InternalProfileHandler(m.getProfile())))
|
.get(InternalProfileHandler.Path, sane(new InternalProfileHandler(m.getProfile())))
|
||||||
|
|
||||||
// Registration endpoints
|
// Registration endpoints
|
||||||
.post(Register3pidRequestTokenHandler.Path,
|
.post(Register3pidRequestTokenHandler.Path,
|
||||||
SaneHandler.around(new Register3pidRequestTokenHandler(m.getReg(), m.getClientDns(), m.getHttpClient())))
|
sane(new Register3pidRequestTokenHandler(m.getReg(), m.getClientDns(), m.getHttpClient())))
|
||||||
|
|
||||||
// Invite endpoints
|
// Invite endpoints
|
||||||
.post(RoomInviteHandler.Path, SaneHandler.around(new RoomInviteHandler(m.getHttpClient(), m.getClientDns(), m.getInvite())))
|
.post(RoomInviteHandler.Path, sane(new RoomInviteHandler(m.getHttpClient(), m.getClientDns(), m.getInvite())))
|
||||||
|
|
||||||
// Application Service endpoints
|
// Application Service endpoints
|
||||||
.get(AsUserHandler.Path, asUserHandler)
|
.get(AsUserHandler.Path, asUserHandler)
|
||||||
@@ -139,13 +142,19 @@ public class HttpMxisd {
|
|||||||
.put("/transactions/{" + AsTransactionHandler.ID + "}", asTxnHandler) // Legacy endpoint
|
.put("/transactions/{" + AsTransactionHandler.ID + "}", asTxnHandler) // Legacy endpoint
|
||||||
|
|
||||||
// Banned endpoints
|
// Banned endpoints
|
||||||
.get(InternalInfoHandler.Path, SaneHandler.around(new InternalInfoHandler()));
|
.get(InternalInfoHandler.Path, sane(new InternalInfoHandler()));
|
||||||
keyEndpoints(handler);
|
keyEndpoints(handler);
|
||||||
identityEndpoints(handler);
|
identityEndpoints(handler);
|
||||||
termsEndpoints(handler);
|
termsEndpoints(handler);
|
||||||
hashEndpoints(handler);
|
hashEndpoints(handler);
|
||||||
accountEndpoints(handler);
|
accountEndpoints(handler);
|
||||||
httpSrv = Undertow.builder().addHttpListener(m.getConfig().getServer().getPort(), "0.0.0.0").setHandler(handler).build();
|
|
||||||
|
if (m.getConfig().getInternal().isEnabled()) {
|
||||||
|
handler.get(InternalInviteManagerHandler.PATH, new InternalInviteManagerHandler(m.getInvite()));
|
||||||
|
}
|
||||||
|
|
||||||
|
ServerConfig serverConfig = m.getConfig().getServer();
|
||||||
|
httpSrv = Undertow.builder().addHttpListener(serverConfig.getPort(), serverConfig.getHostname()).setHandler(handler).build();
|
||||||
|
|
||||||
httpSrv.start();
|
httpSrv.start();
|
||||||
}
|
}
|
||||||
@@ -189,23 +198,32 @@ public class HttpMxisd {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void accountEndpoints(RoutingHandler routingHandler) {
|
private void accountEndpoints(RoutingHandler routingHandler) {
|
||||||
routingHandler.post(AccountRegisterHandler.Path, SaneHandler.around(new AccountRegisterHandler(m.getAccMgr())));
|
MatrixConfig matrixConfig = m.getConfig().getMatrix();
|
||||||
wrapWithTokenAndAuthorizationHandlers(routingHandler, Methods.GET, sane(new AccountGetUserInfoHandler(m.getAccMgr())),
|
if (matrixConfig.isV2()) {
|
||||||
AccountGetUserInfoHandler.Path, true);
|
routingHandler.post(AccountRegisterHandler.Path, sane(new AccountRegisterHandler(m.getAccMgr())));
|
||||||
wrapWithTokenAndAuthorizationHandlers(routingHandler, Methods.GET, sane(new AccountLogoutHandler(m.getAccMgr())),
|
wrapWithTokenAndAuthorizationHandlers(routingHandler, Methods.GET, new AccountGetUserInfoHandler(m.getAccMgr()),
|
||||||
AccountLogoutHandler.Path, true);
|
AccountGetUserInfoHandler.Path, true);
|
||||||
|
wrapWithTokenAndAuthorizationHandlers(routingHandler, Methods.GET, new AccountLogoutHandler(m.getAccMgr()),
|
||||||
|
AccountLogoutHandler.Path, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void termsEndpoints(RoutingHandler routingHandler) {
|
private void termsEndpoints(RoutingHandler routingHandler) {
|
||||||
routingHandler.get(GetTermsHandler.PATH, new GetTermsHandler(m.getConfig().getPolicy()));
|
MatrixConfig matrixConfig = m.getConfig().getMatrix();
|
||||||
routingHandler.post(AcceptTermsHandler.PATH, sane(new AcceptTermsHandler(m.getAccMgr())));
|
if (matrixConfig.isV2()) {
|
||||||
|
routingHandler.get(GetTermsHandler.PATH, sane(new GetTermsHandler(m.getConfig().getPolicy())));
|
||||||
|
routingHandler.post(AcceptTermsHandler.PATH, sane(new AcceptTermsHandler(m.getAccMgr())));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void hashEndpoints(RoutingHandler routingHandler) {
|
private void hashEndpoints(RoutingHandler routingHandler) {
|
||||||
wrapWithTokenAndAuthorizationHandlers(routingHandler, Methods.GET, sane(new HashDetailsHandler(m.getHashManager())),
|
MatrixConfig matrixConfig = m.getConfig().getMatrix();
|
||||||
HashDetailsHandler.PATH, true);
|
if (matrixConfig.isV2()) {
|
||||||
wrapWithTokenAndAuthorizationHandlers(routingHandler, Methods.POST,
|
wrapWithTokenAndAuthorizationHandlers(routingHandler, Methods.GET, new HashDetailsHandler(m.getHashManager()),
|
||||||
sane(new HashLookupHandler(m.getIdentity(), m.getHashManager())), HashLookupHandler.Path, true);
|
HashDetailsHandler.PATH, true);
|
||||||
|
wrapWithTokenAndAuthorizationHandlers(routingHandler, Methods.POST,
|
||||||
|
new HashLookupHandler(m.getIdentity(), m.getHashManager()), HashLookupHandler.Path, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addEndpoints(RoutingHandler routingHandler, HttpString method, boolean useAuthorization, ApiHandler... handlers) {
|
private void addEndpoints(RoutingHandler routingHandler, HttpString method, boolean useAuthorization, ApiHandler... handlers) {
|
||||||
@@ -218,11 +236,11 @@ public class HttpMxisd {
|
|||||||
HttpHandler httpHandler) {
|
HttpHandler httpHandler) {
|
||||||
MatrixConfig matrixConfig = m.getConfig().getMatrix();
|
MatrixConfig matrixConfig = m.getConfig().getMatrix();
|
||||||
if (matrixConfig.isV1()) {
|
if (matrixConfig.isV1()) {
|
||||||
routingHandler.add(method, apiHandler.getPath(IdentityServiceAPI.V1), httpHandler);
|
routingHandler.add(method, apiHandler.getPath(IdentityServiceAPI.V1), sane(httpHandler));
|
||||||
}
|
}
|
||||||
if (matrixConfig.isV2()) {
|
if (matrixConfig.isV2()) {
|
||||||
String path = apiHandler.getPath(IdentityServiceAPI.V2);
|
wrapWithTokenAndAuthorizationHandlers(routingHandler, method, httpHandler, apiHandler.getPath(IdentityServiceAPI.V2),
|
||||||
wrapWithTokenAndAuthorizationHandlers(routingHandler, method, httpHandler, path, useAuthorization);
|
useAuthorization);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,7 +254,7 @@ public class HttpMxisd {
|
|||||||
} else {
|
} else {
|
||||||
wrappedHandler = httpHandler;
|
wrappedHandler = httpHandler;
|
||||||
}
|
}
|
||||||
routingHandler.add(method, url, wrappedHandler);
|
routingHandler.add(method, url, sane(wrappedHandler));
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@@ -256,6 +274,11 @@ public class HttpMxisd {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private HttpHandler sane(HttpHandler httpHandler) {
|
private HttpHandler sane(HttpHandler httpHandler) {
|
||||||
return SaneHandler.around(httpHandler);
|
SaneHandler handler = SaneHandler.around(httpHandler);
|
||||||
|
if (m.getConfig().getLogging().isRequests()) {
|
||||||
|
return new RequestDumpingHandler(handler);
|
||||||
|
} else {
|
||||||
|
return handler;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -27,6 +27,7 @@ import io.kamax.mxisd.auth.AuthProviders;
|
|||||||
import io.kamax.mxisd.backend.IdentityStoreSupplier;
|
import io.kamax.mxisd.backend.IdentityStoreSupplier;
|
||||||
import io.kamax.mxisd.backend.sql.synapse.Synapse;
|
import io.kamax.mxisd.backend.sql.synapse.Synapse;
|
||||||
import io.kamax.mxisd.config.MxisdConfig;
|
import io.kamax.mxisd.config.MxisdConfig;
|
||||||
|
import io.kamax.mxisd.config.StorageConfig;
|
||||||
import io.kamax.mxisd.crypto.CryptoFactory;
|
import io.kamax.mxisd.crypto.CryptoFactory;
|
||||||
import io.kamax.mxisd.crypto.KeyManager;
|
import io.kamax.mxisd.crypto.KeyManager;
|
||||||
import io.kamax.mxisd.crypto.SignatureManager;
|
import io.kamax.mxisd.crypto.SignatureManager;
|
||||||
@@ -54,7 +55,7 @@ import io.kamax.mxisd.registration.RegistrationManager;
|
|||||||
import io.kamax.mxisd.session.SessionManager;
|
import io.kamax.mxisd.session.SessionManager;
|
||||||
import io.kamax.mxisd.storage.IStorage;
|
import io.kamax.mxisd.storage.IStorage;
|
||||||
import io.kamax.mxisd.storage.ormlite.OrmLiteSqlStorage;
|
import io.kamax.mxisd.storage.ormlite.OrmLiteSqlStorage;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.http.impl.client.CloseableHttpClient;
|
import org.apache.http.impl.client.CloseableHttpClient;
|
||||||
import org.apache.http.impl.client.HttpClients;
|
import org.apache.http.impl.client.HttpClients;
|
||||||
|
|
||||||
@@ -66,7 +67,7 @@ public class Mxisd {
|
|||||||
public static final String Version = StringUtils.defaultIfBlank(Mxisd.class.getPackage().getImplementationVersion(), "UNKNOWN");
|
public static final String Version = StringUtils.defaultIfBlank(Mxisd.class.getPackage().getImplementationVersion(), "UNKNOWN");
|
||||||
public static final String Agent = Name + "/" + Version;
|
public static final String Agent = Name + "/" + Version;
|
||||||
|
|
||||||
private MxisdConfig cfg;
|
private final MxisdConfig cfg;
|
||||||
|
|
||||||
private CloseableHttpClient httpClient;
|
private CloseableHttpClient httpClient;
|
||||||
private IRemoteIdentityServerFetcher srvFetcher;
|
private IRemoteIdentityServerFetcher srvFetcher;
|
||||||
@@ -109,7 +110,10 @@ public class Mxisd {
|
|||||||
IdentityServerUtils.setHttpClient(httpClient);
|
IdentityServerUtils.setHttpClient(httpClient);
|
||||||
srvFetcher = new RemoteIdentityServerFetcher(httpClient);
|
srvFetcher = new RemoteIdentityServerFetcher(httpClient);
|
||||||
|
|
||||||
store = new OrmLiteSqlStorage(cfg);
|
StorageConfig.BackendEnum storageBackend = cfg.getStorage().getBackend();
|
||||||
|
StorageConfig.Provider storageProvider = cfg.getStorage().getProvider();
|
||||||
|
store = new OrmLiteSqlStorage(storageBackend, storageProvider);
|
||||||
|
|
||||||
keyMgr = CryptoFactory.getKeyManager(cfg.getKey());
|
keyMgr = CryptoFactory.getKeyManager(cfg.getKey());
|
||||||
signMgr = CryptoFactory.getSignatureManager(cfg, keyMgr);
|
signMgr = CryptoFactory.getSignatureManager(cfg, keyMgr);
|
||||||
clientDns = new ClientDnsOverwrite(cfg.getDns().getOverwrite());
|
clientDns = new ClientDnsOverwrite(cfg.getDns().getOverwrite());
|
||||||
|
@@ -36,7 +36,7 @@ public class MxisdStandaloneExec {
|
|||||||
private static final Logger log = LoggerFactory.getLogger("App");
|
private static final Logger log = LoggerFactory.getLogger("App");
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
String logLevel = System.getenv("MA1SD_LOG_LEVEL");
|
String logLevel = System.getenv("MXIDS_LOG_LEVEL");
|
||||||
if (StringUtils.isNotBlank(logLevel)) {
|
if (StringUtils.isNotBlank(logLevel)) {
|
||||||
System.setProperty("org.slf4j.simpleLogger.log.io.kamax.mxisd", logLevel);
|
System.setProperty("org.slf4j.simpleLogger.log.io.kamax.mxisd", logLevel);
|
||||||
}
|
}
|
||||||
@@ -44,49 +44,72 @@ public class MxisdStandaloneExec {
|
|||||||
try {
|
try {
|
||||||
MxisdConfig cfg = null;
|
MxisdConfig cfg = null;
|
||||||
Iterator<String> argsIt = Arrays.asList(args).iterator();
|
Iterator<String> argsIt = Arrays.asList(args).iterator();
|
||||||
|
boolean dump = false;
|
||||||
|
boolean exit = false;
|
||||||
while (argsIt.hasNext()) {
|
while (argsIt.hasNext()) {
|
||||||
String arg = argsIt.next();
|
String arg = argsIt.next();
|
||||||
if (StringUtils.equalsAny(arg, "-h", "--help", "-?", "--usage")) {
|
switch (arg) {
|
||||||
System.out.println("Available arguments:" + System.lineSeparator());
|
case "-h":
|
||||||
System.out.println(" -h, --help Show this help message");
|
case "--help":
|
||||||
System.out.println(" --version Print the version then exit");
|
case "-?":
|
||||||
System.out.println(" -c, --config Set the configuration file location");
|
case "--usage":
|
||||||
System.out.println(" -v Increase log level (log more info)");
|
System.out.println("Available arguments:" + System.lineSeparator());
|
||||||
System.out.println(" -vv Further increase log level");
|
System.out.println(" -h, --help Show this help message");
|
||||||
System.out.println(" ");
|
System.out.println(" --version Print the version then exit");
|
||||||
System.exit(0);
|
System.out.println(" -c, --config Set the configuration file location");
|
||||||
} else if (StringUtils.equals(arg, "-v")) {
|
System.out.println(" -v Increase log level (log more info)");
|
||||||
System.setProperty("org.slf4j.simpleLogger.log.io.kamax.mxisd", "debug");
|
System.out.println(" -vv Further increase log level");
|
||||||
} else if (StringUtils.equals(arg, "-vv")) {
|
System.out.println(" --dump Dump the full mxids configuration");
|
||||||
System.setProperty("org.slf4j.simpleLogger.log.io.kamax.mxisd", "trace");
|
System.out.println(" --dump-and-exit Dump the full mxids configuration and exit");
|
||||||
} else if (StringUtils.equalsAny(arg, "-c", "--config")) {
|
System.out.println(" ");
|
||||||
String cfgFile = argsIt.next();
|
System.exit(0);
|
||||||
cfg = YamlConfigLoader.loadFromFile(cfgFile);
|
return;
|
||||||
} else if (StringUtils.equals("--version", arg)) {
|
case "-v":
|
||||||
System.out.println(Mxisd.Version);
|
System.setProperty("org.slf4j.simpleLogger.log.io.kamax.mxisd", "debug");
|
||||||
System.exit(0);
|
break;
|
||||||
} else {
|
case "-vv":
|
||||||
System.err.println("Invalid argument: " + arg);
|
System.setProperty("org.slf4j.simpleLogger.log.io.kamax.mxisd", "trace");
|
||||||
System.err.println("Try '--help' for available arguments");
|
break;
|
||||||
System.exit(1);
|
case "-c":
|
||||||
|
case "--config":
|
||||||
|
String cfgFile = argsIt.next();
|
||||||
|
cfg = YamlConfigLoader.loadFromFile(cfgFile);
|
||||||
|
break;
|
||||||
|
case "--dump-and-exit":
|
||||||
|
exit = true;
|
||||||
|
case "--dump":
|
||||||
|
dump = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
System.err.println("Invalid argument: " + arg);
|
||||||
|
System.err.println("Try '--help' for available arguments");
|
||||||
|
System.exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Objects.isNull(cfg)) {
|
if (Objects.isNull(cfg)) {
|
||||||
cfg = YamlConfigLoader.tryLoadFromFile("ma1sd.yaml").orElseGet(MxisdConfig::new);
|
cfg = YamlConfigLoader.tryLoadFromFile("mxids.yaml").orElseGet(MxisdConfig::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info("ma1sd starting");
|
if (dump) {
|
||||||
|
String outputPath = "mxids.yaml";
|
||||||
|
YamlConfigLoader.dumpConfig(cfg, outputPath);
|
||||||
|
if (exit) {
|
||||||
|
System.exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("mxids starting");
|
||||||
log.info("Version: {}", Mxisd.Version);
|
log.info("Version: {}", Mxisd.Version);
|
||||||
|
|
||||||
HttpMxisd mxisd = new HttpMxisd(cfg);
|
HttpMxisd mxisd = new HttpMxisd(cfg);
|
||||||
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
||||||
mxisd.stop();
|
mxisd.stop();
|
||||||
log.info("ma1sd stopped");
|
log.info("mxids stopped");
|
||||||
}));
|
}));
|
||||||
mxisd.start();
|
mxisd.start();
|
||||||
|
|
||||||
log.info("ma1sd started");
|
log.info("mxids started");
|
||||||
} catch (ConfigurationException e) {
|
} catch (ConfigurationException e) {
|
||||||
log.error(e.getDetailedMessage());
|
log.error(e.getDetailedMessage());
|
||||||
log.error(e.getMessage());
|
log.error(e.getMessage());
|
||||||
|
@@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
package io.kamax.mxisd;
|
package io.kamax.mxisd;
|
||||||
|
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
// FIXME consider integrating in matrix-java-sdk?
|
// FIXME consider integrating in matrix-java-sdk?
|
||||||
public enum UserIdType {
|
public enum UserIdType {
|
||||||
|
@@ -48,6 +48,7 @@ import org.slf4j.LoggerFactory;
|
|||||||
import org.yaml.snakeyaml.Yaml;
|
import org.yaml.snakeyaml.Yaml;
|
||||||
import org.yaml.snakeyaml.introspector.BeanAccess;
|
import org.yaml.snakeyaml.introspector.BeanAccess;
|
||||||
import org.yaml.snakeyaml.representer.Representer;
|
import org.yaml.snakeyaml.representer.Representer;
|
||||||
|
import org.yaml.snakeyaml.DumperOptions;
|
||||||
|
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -142,18 +143,18 @@ public class AppSvcManager {
|
|||||||
String synapseRegFile = cfg.getAppsvc().getRegistration().getSynapse().getFile();
|
String synapseRegFile = cfg.getAppsvc().getRegistration().getSynapse().getFile();
|
||||||
|
|
||||||
if (StringUtils.isBlank(synapseRegFile)) {
|
if (StringUtils.isBlank(synapseRegFile)) {
|
||||||
log.info("No synapse registration file path given - skipping generation...");
|
log.info("No synapse registration file path given - skipping generation...");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
SynapseRegistrationYaml syncCfg = SynapseRegistrationYaml.parse(cfg.getAppsvc(), cfg.getMatrix().getDomain());
|
SynapseRegistrationYaml syncCfg = SynapseRegistrationYaml.parse(cfg.getAppsvc(), cfg.getMatrix().getDomain());
|
||||||
|
|
||||||
Representer rep = new Representer();
|
DumperOptions options = new DumperOptions();
|
||||||
|
options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); // Set YAML flow style to block
|
||||||
|
Representer rep = new Representer(options);
|
||||||
rep.getPropertyUtils().setBeanAccess(BeanAccess.FIELD);
|
rep.getPropertyUtils().setBeanAccess(BeanAccess.FIELD);
|
||||||
Yaml yaml = new Yaml(rep);
|
Yaml yaml = new Yaml(rep);
|
||||||
|
|
||||||
// SnakeYAML set the type of object on the first line, which can fail to be parsed on synapse
|
|
||||||
// We therefore need to split the resulting string, remove the first line, and then write it
|
|
||||||
List<String> lines = new ArrayList<>(Arrays.asList(yaml.dump(syncCfg).split("\\R+")));
|
List<String> lines = new ArrayList<>(Arrays.asList(yaml.dump(syncCfg).split("\\R+")));
|
||||||
if (StringUtils.equals(lines.get(0), "!!" + SynapseRegistrationYaml.class.getCanonicalName())) {
|
if (StringUtils.equals(lines.get(0), "!!" + SynapseRegistrationYaml.class.getCanonicalName())) {
|
||||||
lines.remove(0);
|
lines.remove(0);
|
||||||
|
@@ -25,8 +25,8 @@ import io.kamax.matrix.hs._MatrixRoom;
|
|||||||
import io.kamax.mxisd.Mxisd;
|
import io.kamax.mxisd.Mxisd;
|
||||||
import io.kamax.mxisd.invitation.IThreePidInviteReply;
|
import io.kamax.mxisd.invitation.IThreePidInviteReply;
|
||||||
import org.apache.commons.cli.CommandLine;
|
import org.apache.commons.cli.CommandLine;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.commons.lang.text.StrBuilder;
|
import org.apache.commons.lang3.text.StrBuilder;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@@ -25,7 +25,7 @@ import io.kamax.matrix.hs._MatrixRoom;
|
|||||||
import io.kamax.mxisd.Mxisd;
|
import io.kamax.mxisd.Mxisd;
|
||||||
import io.kamax.mxisd.lookup.SingleLookupReply;
|
import io.kamax.mxisd.lookup.SingleLookupReply;
|
||||||
import org.apache.commons.cli.CommandLine;
|
import org.apache.commons.cli.CommandLine;
|
||||||
import org.apache.commons.lang.text.StrBuilder;
|
import org.apache.commons.lang3.text.StrBuilder;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
@@ -34,7 +34,7 @@ import io.kamax.mxisd.invitation.IMatrixIdInvite;
|
|||||||
import io.kamax.mxisd.invitation.MatrixIdInvite;
|
import io.kamax.mxisd.invitation.MatrixIdInvite;
|
||||||
import io.kamax.mxisd.notification.NotificationManager;
|
import io.kamax.mxisd.notification.NotificationManager;
|
||||||
import io.kamax.mxisd.profile.ProfileManager;
|
import io.kamax.mxisd.profile.ProfileManager;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@@ -144,7 +144,13 @@ public class MembershipEventProcessor implements EventTypeProcessor {
|
|||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
log.info("Found {} email(s) in identity store for {}", tpids.size(), inviteeId);
|
log.info("Found {} email(s) in identity store for {}", tpids.size(), inviteeId);
|
||||||
|
|
||||||
for (_ThreePid tpid : tpids) {
|
log.info("Removing duplicates from identity store");
|
||||||
|
List<_ThreePid> uniqueTpids = tpids.stream()
|
||||||
|
.distinct()
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
log.info("There are {} unique email(s) in identity store for {}", uniqueTpids.size(), inviteeId);
|
||||||
|
|
||||||
|
for (_ThreePid tpid : uniqueTpids) {
|
||||||
log.info("Found Email to notify about room invitation: {}", tpid.getAddress());
|
log.info("Found Email to notify about room invitation: {}", tpid.getAddress());
|
||||||
Map<String, String> properties = new HashMap<>();
|
Map<String, String> properties = new HashMap<>();
|
||||||
profiler.getDisplayName(sender).ifPresent(name -> properties.put("sender_display_name", name));
|
profiler.getDisplayName(sender).ifPresent(name -> properties.put("sender_display_name", name));
|
||||||
|
@@ -32,7 +32,7 @@ import io.kamax.mxisd.as.processor.command.InviteCommandProcessor;
|
|||||||
import io.kamax.mxisd.as.processor.command.LookupCommandProcessor;
|
import io.kamax.mxisd.as.processor.command.LookupCommandProcessor;
|
||||||
import io.kamax.mxisd.as.processor.command.PingCommandProcessor;
|
import io.kamax.mxisd.as.processor.command.PingCommandProcessor;
|
||||||
import org.apache.commons.cli.*;
|
import org.apache.commons.cli.*;
|
||||||
import org.apache.commons.lang.text.StrBuilder;
|
import org.apache.commons.lang3.text.StrBuilder;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
@@ -10,6 +10,7 @@ import io.kamax.mxisd.exception.BadRequestException;
|
|||||||
import io.kamax.mxisd.exception.InvalidCredentialsException;
|
import io.kamax.mxisd.exception.InvalidCredentialsException;
|
||||||
import io.kamax.mxisd.exception.NotFoundException;
|
import io.kamax.mxisd.exception.NotFoundException;
|
||||||
import io.kamax.mxisd.matrix.HomeserverFederationResolver;
|
import io.kamax.mxisd.matrix.HomeserverFederationResolver;
|
||||||
|
import io.kamax.mxisd.matrix.HomeserverVerifier;
|
||||||
import io.kamax.mxisd.storage.IStorage;
|
import io.kamax.mxisd.storage.IStorage;
|
||||||
import io.kamax.mxisd.storage.ormlite.dao.AccountDao;
|
import io.kamax.mxisd.storage.ormlite.dao.AccountDao;
|
||||||
import org.apache.http.HttpStatus;
|
import org.apache.http.HttpStatus;
|
||||||
@@ -22,18 +23,10 @@ import org.slf4j.Logger;
|
|||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.security.cert.Certificate;
|
|
||||||
import java.security.cert.CertificateParsingException;
|
|
||||||
import java.security.cert.X509Certificate;
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import javax.net.ssl.HostnameVerifier;
|
|
||||||
import javax.net.ssl.SSLPeerUnverifiedException;
|
|
||||||
import javax.net.ssl.SSLSession;
|
|
||||||
|
|
||||||
public class AccountManager {
|
public class AccountManager {
|
||||||
|
|
||||||
@@ -80,7 +73,7 @@ public class AccountManager {
|
|||||||
homeserverURL + "/_matrix/federation/v1/openid/userinfo?access_token=" + openIdToken.getAccessToken());
|
homeserverURL + "/_matrix/federation/v1/openid/userinfo?access_token=" + openIdToken.getAccessToken());
|
||||||
String userId;
|
String userId;
|
||||||
try (CloseableHttpClient httpClient = HttpClients.custom()
|
try (CloseableHttpClient httpClient = HttpClients.custom()
|
||||||
.setSSLHostnameVerifier(new MatrixHostnameVerifier(homeserverTarget.getDomain())).build()) {
|
.setSSLHostnameVerifier(new HomeserverVerifier(homeserverTarget.getDomain())).build()) {
|
||||||
try (CloseableHttpResponse response = httpClient.execute(getUserInfo)) {
|
try (CloseableHttpResponse response = httpClient.execute(getUserInfo)) {
|
||||||
int statusCode = response.getStatusLine().getStatusCode();
|
int statusCode = response.getStatusLine().getStatusCode();
|
||||||
if (statusCode == HttpStatus.SC_OK) {
|
if (statusCode == HttpStatus.SC_OK) {
|
||||||
@@ -170,74 +163,4 @@ public class AccountManager {
|
|||||||
public MatrixConfig getMatrixConfig() {
|
public MatrixConfig getMatrixConfig() {
|
||||||
return matrixConfig;
|
return matrixConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class MatrixHostnameVerifier implements HostnameVerifier {
|
|
||||||
|
|
||||||
private static final String ALT_DNS_NAME_TYPE = "2";
|
|
||||||
private static final String ALT_IP_ADDRESS_TYPE = "7";
|
|
||||||
|
|
||||||
private final String matrixHostname;
|
|
||||||
|
|
||||||
public MatrixHostnameVerifier(String matrixHostname) {
|
|
||||||
this.matrixHostname = matrixHostname;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean verify(String hostname, SSLSession session) {
|
|
||||||
try {
|
|
||||||
Certificate peerCertificate = session.getPeerCertificates()[0];
|
|
||||||
if (peerCertificate instanceof X509Certificate) {
|
|
||||||
X509Certificate x509Certificate = (X509Certificate) peerCertificate;
|
|
||||||
if (x509Certificate.getSubjectAlternativeNames() == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (String altSubjectName : getAltSubjectNames(x509Certificate)) {
|
|
||||||
if (match(altSubjectName)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (SSLPeerUnverifiedException | CertificateParsingException e) {
|
|
||||||
LOGGER.error("Unable to check remote host", e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<String> getAltSubjectNames(X509Certificate x509Certificate) {
|
|
||||||
List<String> subjectNames = new ArrayList<>();
|
|
||||||
try {
|
|
||||||
for (List<?> subjectAlternativeNames : x509Certificate.getSubjectAlternativeNames()) {
|
|
||||||
if (subjectAlternativeNames == null
|
|
||||||
|| subjectAlternativeNames.size() < 2
|
|
||||||
|| subjectAlternativeNames.get(0) == null
|
|
||||||
|| subjectAlternativeNames.get(1) == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
String subjectType = subjectAlternativeNames.get(0).toString();
|
|
||||||
switch (subjectType) {
|
|
||||||
case ALT_DNS_NAME_TYPE:
|
|
||||||
case ALT_IP_ADDRESS_TYPE:
|
|
||||||
subjectNames.add(subjectAlternativeNames.get(1).toString());
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
LOGGER.trace("Unusable subject type: " + subjectType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (CertificateParsingException e) {
|
|
||||||
LOGGER.error("Unable to parse the certificate", e);
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
return subjectNames;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean match(String altSubjectName) {
|
|
||||||
if (altSubjectName.startsWith("*.")) {
|
|
||||||
return altSubjectName.toLowerCase().endsWith(matrixHostname.toLowerCase());
|
|
||||||
} else {
|
|
||||||
return matrixHostname.equalsIgnoreCase(altSubjectName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -45,7 +45,7 @@ import io.kamax.mxisd.lookup.ThreePidMapping;
|
|||||||
import io.kamax.mxisd.lookup.strategy.LookupStrategy;
|
import io.kamax.mxisd.lookup.strategy.LookupStrategy;
|
||||||
import io.kamax.mxisd.util.RestClientUtils;
|
import io.kamax.mxisd.util.RestClientUtils;
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.http.HttpEntity;
|
import org.apache.http.HttpEntity;
|
||||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||||
import org.apache.http.client.methods.HttpPost;
|
import org.apache.http.client.methods.HttpPost;
|
||||||
|
@@ -27,7 +27,7 @@ import io.kamax.mxisd.config.ExecConfig;
|
|||||||
import io.kamax.mxisd.profile.JsonProfileRequest;
|
import io.kamax.mxisd.profile.JsonProfileRequest;
|
||||||
import io.kamax.mxisd.profile.JsonProfileResult;
|
import io.kamax.mxisd.profile.JsonProfileResult;
|
||||||
import io.kamax.mxisd.profile.ProfileProvider;
|
import io.kamax.mxisd.profile.ProfileProvider;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@@ -1,168 +1,81 @@
|
|||||||
/*
|
|
||||||
* mxisd - Matrix Identity Server Daemon
|
|
||||||
* 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.mxisd.backend.firebase;
|
package io.kamax.mxisd.backend.firebase;
|
||||||
|
|
||||||
import com.google.firebase.auth.UserInfo;
|
import com.google.firebase.auth.FirebaseAuth;
|
||||||
import com.google.i18n.phonenumbers.NumberParseException;
|
import com.google.firebase.auth.FirebaseAuthException;
|
||||||
import com.google.i18n.phonenumbers.PhoneNumberUtil;
|
import com.google.firebase.auth.FirebaseToken;
|
||||||
import io.kamax.matrix.ThreePid;
|
import com.google.firebase.auth.UserRecord;
|
||||||
import io.kamax.matrix.ThreePidMedium;
|
|
||||||
import io.kamax.matrix._MatrixID;
|
import io.kamax.matrix._MatrixID;
|
||||||
import io.kamax.mxisd.UserIdType;
|
|
||||||
import io.kamax.mxisd.auth.provider.AuthenticatorProvider;
|
import io.kamax.mxisd.auth.provider.AuthenticatorProvider;
|
||||||
import io.kamax.mxisd.auth.provider.BackendAuthResult;
|
import io.kamax.mxisd.auth.provider.BackendAuthResult;
|
||||||
import io.kamax.mxisd.config.FirebaseConfig;
|
import io.kamax.mxisd.config.FirebaseConfig;
|
||||||
import org.apache.commons.lang.StringUtils;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
|
||||||
public class GoogleFirebaseAuthenticator extends GoogleFirebaseBackend implements AuthenticatorProvider {
|
public class GoogleFirebaseAuthenticator implements AuthenticatorProvider {
|
||||||
|
|
||||||
private transient final Logger log = LoggerFactory.getLogger(GoogleFirebaseAuthenticator.class);
|
private static final Logger log = LoggerFactory.getLogger(GoogleFirebaseAuthenticator.class);
|
||||||
|
private static final ExecutorService executor = Executors.newCachedThreadPool(); // Consider using a fixed thread pool or other strategies based on your app's needs
|
||||||
|
|
||||||
private PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
|
private FirebaseConfig config;
|
||||||
|
|
||||||
public GoogleFirebaseAuthenticator(FirebaseConfig cfg) {
|
public GoogleFirebaseAuthenticator(FirebaseConfig config) {
|
||||||
this(cfg.isEnabled(), cfg.getCredentials(), cfg.getDatabase());
|
this.config = config;
|
||||||
}
|
}
|
||||||
|
|
||||||
public GoogleFirebaseAuthenticator(boolean isEnabled, String credsPath, String db) {
|
@Override
|
||||||
super(isEnabled, "AuthenticationProvider", credsPath, db);
|
public boolean isEnabled() {
|
||||||
}
|
return this.config.isEnabled();
|
||||||
|
|
||||||
private void waitOnLatch(BackendAuthResult result, CountDownLatch l, String purpose) {
|
|
||||||
try {
|
|
||||||
l.await(30, TimeUnit.SECONDS);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
log.warn("Interrupted while waiting for " + purpose);
|
|
||||||
result.fail();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void toEmail(BackendAuthResult result, String email) {
|
|
||||||
if (StringUtils.isBlank(email)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
result.withThreePid(new ThreePid(ThreePidMedium.Email.getId(), email));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void toMsisdn(BackendAuthResult result, String phoneNumber) {
|
|
||||||
if (StringUtils.isBlank(phoneNumber)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
String number = phoneUtil.format(
|
|
||||||
phoneUtil.parse(
|
|
||||||
phoneNumber,
|
|
||||||
null // No default region
|
|
||||||
),
|
|
||||||
PhoneNumberUtil.PhoneNumberFormat.E164
|
|
||||||
).substring(1); // We want without the leading +
|
|
||||||
result.withThreePid(new ThreePid(ThreePidMedium.PhoneNumber.getId(), number));
|
|
||||||
} catch (NumberParseException e) {
|
|
||||||
log.warn("Invalid phone number: {}", phoneNumber);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void waitOnLatch(CountDownLatch l) {
|
|
||||||
try {
|
|
||||||
l.await(30, TimeUnit.SECONDS);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
log.warn("Interrupted while waiting for Firebase auth check");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BackendAuthResult authenticate(_MatrixID mxid, String password) {
|
public BackendAuthResult authenticate(_MatrixID mxid, String password) {
|
||||||
if (!isEnabled()) {
|
if (!isEnabled()) {
|
||||||
throw new IllegalStateException();
|
log.warn("Firebase authenticator is disabled.");
|
||||||
|
return BackendAuthResult.failure();
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info("Trying to authenticate {}", mxid);
|
CompletableFuture<BackendAuthResult> resultFuture = new CompletableFuture<>();
|
||||||
|
executor.submit(() -> {
|
||||||
final BackendAuthResult result = BackendAuthResult.failure();
|
|
||||||
|
|
||||||
String localpart = mxid.getLocalPart();
|
|
||||||
CountDownLatch l = new CountDownLatch(1);
|
|
||||||
getFirebase().verifyIdToken(password).addOnSuccessListener(token -> {
|
|
||||||
try {
|
try {
|
||||||
if (!StringUtils.equals(localpart, token.getUid())) {
|
FirebaseToken decodedToken = FirebaseAuth.getInstance().verifyIdToken(password);
|
||||||
log.info("Failure to authenticate {}: Matrix ID localpart '{}' does not match Firebase UID '{}'", mxid, localpart, token.getUid());
|
if (!mxid.getLocalPart().equals(decodedToken.getUid())) {
|
||||||
result.fail();
|
log.warn("UID mismatch for user {}", mxid);
|
||||||
|
resultFuture.complete(BackendAuthResult.failure());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
result.succeed(mxid.getId(), UserIdType.MatrixID.getId(), token.getName());
|
// Assuming you have a method to convert Firebase user info into BackendAuthResult
|
||||||
log.info("{} was successfully authenticated", mxid);
|
resultFuture.complete(convertToAuthResult(decodedToken));
|
||||||
log.info("Fetching profile for {}", mxid);
|
} catch (FirebaseAuthException e) {
|
||||||
CountDownLatch userRecordLatch = new CountDownLatch(1);
|
log.error("Failed to authenticate user {}: {}", mxid, e.getMessage(), e);
|
||||||
getFirebase().getUser(token.getUid()).addOnSuccessListener(user -> {
|
resultFuture.complete(BackendAuthResult.failure());
|
||||||
try {
|
|
||||||
toEmail(result, user.getEmail());
|
|
||||||
toMsisdn(result, user.getPhoneNumber());
|
|
||||||
|
|
||||||
for (UserInfo info : user.getProviderData()) {
|
|
||||||
toEmail(result, info.getEmail());
|
|
||||||
toMsisdn(result, info.getPhoneNumber());
|
|
||||||
}
|
|
||||||
|
|
||||||
log.info("Got {} 3PIDs in profile", result.getProfile().getThreePids().size());
|
|
||||||
} finally {
|
|
||||||
userRecordLatch.countDown();
|
|
||||||
}
|
|
||||||
}).addOnFailureListener(e -> {
|
|
||||||
try {
|
|
||||||
log.warn("Unable to fetch Firebase user profile for {}", mxid);
|
|
||||||
result.fail();
|
|
||||||
} finally {
|
|
||||||
userRecordLatch.countDown();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
waitOnLatch(result, userRecordLatch, "Firebase user profile");
|
|
||||||
} finally {
|
|
||||||
l.countDown();
|
|
||||||
}
|
|
||||||
}).addOnFailureListener(e -> {
|
|
||||||
try {
|
|
||||||
if (e instanceof IllegalArgumentException) {
|
|
||||||
log.info("Failure to authenticate {}: invalid firebase token", mxid);
|
|
||||||
} else {
|
|
||||||
log.info("Failure to authenticate {}: {}", mxid, e.getMessage(), e);
|
|
||||||
log.info("Exception", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
result.fail();
|
|
||||||
} finally {
|
|
||||||
l.countDown();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
waitOnLatch(result, l, "Firebase auth check");
|
try {
|
||||||
return result;
|
return resultFuture.get(); // This will block, consider using thenAccept or similar for a truly non-blocking approach
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Error during authentication process", e);
|
||||||
|
return BackendAuthResult.failure();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private BackendAuthResult convertToAuthResult(FirebaseToken decodedToken) {
|
||||||
|
String userId = decodedToken.getUid(); // UID from Firebase as the user ID
|
||||||
|
String userIdType = "MatrixID"; // Assuming you're using string literals for user ID types
|
||||||
|
String displayName = decodedToken.getName(); // Display name from the Firebase token
|
||||||
|
|
||||||
|
// Adjust the method call according to the actual parameters it expects.
|
||||||
|
// This example uses three strings directly.
|
||||||
|
return BackendAuthResult.success(userId, userIdType, displayName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure resources are properly released when no longer needed
|
||||||
|
public static void shutdown() {
|
||||||
|
executor.shutdown();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,90 +1,43 @@
|
|||||||
/*
|
|
||||||
* mxisd - Matrix Identity Server Daemon
|
|
||||||
* 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.mxisd.backend.firebase;
|
package io.kamax.mxisd.backend.firebase;
|
||||||
|
|
||||||
|
import com.google.auth.oauth2.GoogleCredentials;
|
||||||
import com.google.firebase.FirebaseApp;
|
import com.google.firebase.FirebaseApp;
|
||||||
import com.google.firebase.FirebaseOptions;
|
import com.google.firebase.FirebaseOptions;
|
||||||
import com.google.firebase.auth.FirebaseAuth;
|
|
||||||
import com.google.firebase.auth.FirebaseCredential;
|
|
||||||
import com.google.firebase.auth.FirebaseCredentials;
|
|
||||||
import com.google.firebase.database.FirebaseDatabase;
|
|
||||||
import org.apache.commons.lang.StringUtils;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
public class GoogleFirebaseBackend {
|
public abstract class GoogleFirebaseBackend {
|
||||||
|
protected boolean enabled;
|
||||||
|
protected String backendName;
|
||||||
|
protected String credentialsPath;
|
||||||
|
protected String databaseUrl;
|
||||||
|
|
||||||
private transient final Logger log = LoggerFactory.getLogger(GoogleFirebaseBackend.class);
|
public GoogleFirebaseBackend(boolean isEnabled, String backendName, String credsPath, String db) {
|
||||||
|
this.enabled = isEnabled;
|
||||||
private boolean isEnabled;
|
this.backendName = backendName;
|
||||||
private FirebaseAuth fbAuth;
|
this.credentialsPath = credsPath;
|
||||||
protected FirebaseDatabase fbDb;
|
this.databaseUrl = db;
|
||||||
|
if (isEnabled) {
|
||||||
GoogleFirebaseBackend(boolean isEnabled, String name, String credsPath, String db) {
|
try {
|
||||||
this.isEnabled = isEnabled;
|
initializeFirebase();
|
||||||
if (!isEnabled) {
|
} catch (IOException e) {
|
||||||
return;
|
throw new RuntimeException("Failed to initialize Firebase", e);
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
FirebaseApp fbApp = FirebaseApp.initializeApp(getOpts(credsPath, db), name);
|
|
||||||
fbAuth = FirebaseAuth.getInstance(fbApp);
|
|
||||||
FirebaseDatabase.getInstance(fbApp);
|
|
||||||
|
|
||||||
log.info("Google Firebase Authentication is ready");
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException("Error when initializing Firebase", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private FirebaseCredential getCreds(String credsPath) throws IOException {
|
|
||||||
if (StringUtils.isNotBlank(credsPath)) {
|
|
||||||
try (FileInputStream is = new FileInputStream(credsPath)) {
|
|
||||||
return FirebaseCredentials.fromCertificate(is);
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
return FirebaseCredentials.applicationDefault();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private FirebaseOptions getOpts(String credsPath, String db) throws IOException {
|
private void initializeFirebase() throws IOException {
|
||||||
if (StringUtils.isBlank(db)) {
|
FileInputStream serviceAccount = new FileInputStream(credentialsPath);
|
||||||
throw new IllegalArgumentException("Firebase database is not configured");
|
|
||||||
}
|
|
||||||
|
|
||||||
return new FirebaseOptions.Builder()
|
FirebaseOptions options = new FirebaseOptions.Builder()
|
||||||
.setCredential(getCreds(credsPath))
|
.setCredentials(GoogleCredentials.fromStream(serviceAccount))
|
||||||
.setDatabaseUrl(db)
|
.setDatabaseUrl(databaseUrl)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
if (FirebaseApp.getApps().isEmpty()) { // Check if Firebase has been initialized already
|
||||||
|
FirebaseApp.initializeApp(options);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FirebaseAuth getFirebase() {
|
// Additional methods for GoogleFirebaseBackend
|
||||||
return fbAuth;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isEnabled() {
|
|
||||||
return isEnabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,28 +1,5 @@
|
|||||||
/*
|
|
||||||
* mxisd - Matrix Identity Server Daemon
|
|
||||||
* 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.mxisd.backend.firebase;
|
package io.kamax.mxisd.backend.firebase;
|
||||||
|
|
||||||
import com.google.firebase.auth.UserRecord;
|
|
||||||
import com.google.firebase.tasks.OnFailureListener;
|
|
||||||
import com.google.firebase.tasks.OnSuccessListener;
|
|
||||||
import io.kamax.matrix.MatrixID;
|
import io.kamax.matrix.MatrixID;
|
||||||
import io.kamax.matrix.ThreePidMedium;
|
import io.kamax.matrix.ThreePidMedium;
|
||||||
import io.kamax.mxisd.config.MxisdConfig;
|
import io.kamax.mxisd.config.MxisdConfig;
|
||||||
@@ -36,25 +13,22 @@ import org.slf4j.LoggerFactory;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
public class GoogleFirebaseProvider extends GoogleFirebaseBackend implements IThreePidProvider {
|
public class GoogleFirebaseProvider extends GoogleFirebaseBackend implements IThreePidProvider {
|
||||||
|
|
||||||
private transient final Logger log = LoggerFactory.getLogger(GoogleFirebaseProvider.class);
|
private final Logger log = LoggerFactory.getLogger(GoogleFirebaseProvider.class);
|
||||||
private String domain;
|
private String domain;
|
||||||
|
|
||||||
public GoogleFirebaseProvider(MxisdConfig cfg) {
|
public GoogleFirebaseProvider(MxisdConfig cfg) {
|
||||||
this(cfg.getFirebase().isEnabled(), cfg.getFirebase().getCredentials(), cfg.getFirebase().getDatabase(), cfg.getMatrix().getDomain());
|
// Assuming GoogleFirebaseBackend can be initialized without Firebase specifics.
|
||||||
|
super(cfg.getFirebase().isEnabled(), cfg.getFirebase().getCredentials(), cfg.getFirebase().getDatabase(), cfg.getMatrix().getDomain());
|
||||||
|
this.domain = cfg.getMatrix().getDomain();
|
||||||
}
|
}
|
||||||
|
|
||||||
public GoogleFirebaseProvider(boolean isEnabled, String credsPath, String db, String domain) {
|
private String getMxid(String uid) {
|
||||||
super(isEnabled, "ThreePidProvider", credsPath, db);
|
// Mock UID to MXID conversion
|
||||||
this.domain = domain;
|
return MatrixID.asAcceptable(uid, domain).getId();
|
||||||
}
|
|
||||||
|
|
||||||
private String getMxid(UserRecord record) {
|
|
||||||
return MatrixID.asAcceptable(record.getUid(), domain).getId();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -67,71 +41,34 @@ public class GoogleFirebaseProvider extends GoogleFirebaseBackend implements ITh
|
|||||||
return 25;
|
return 25;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void waitOnLatch(CountDownLatch l) {
|
private Optional<String> findInternal(String medium, String address) {
|
||||||
try {
|
CompletableFuture<Optional<String>> future = new CompletableFuture<>();
|
||||||
l.await(30, TimeUnit.SECONDS);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
log.warn("Interrupted while waiting for Firebase auth check");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Optional<UserRecord> findInternal(String medium, String address) {
|
// Directly complete with empty to simulate no user found
|
||||||
final UserRecord[] r = new UserRecord[1];
|
future.complete(Optional.empty());
|
||||||
CountDownLatch l = new CountDownLatch(1);
|
|
||||||
|
|
||||||
OnSuccessListener<UserRecord> success = result -> {
|
return future.join(); // Using join to avoid handling InterruptedException
|
||||||
log.info("Found 3PID match for {}:{} - UID is {}", medium, address, result.getUid());
|
|
||||||
r[0] = result;
|
|
||||||
l.countDown();
|
|
||||||
};
|
|
||||||
|
|
||||||
OnFailureListener failure = e -> {
|
|
||||||
log.info("No 3PID match for {}:{} - {}", medium, address, e.getMessage());
|
|
||||||
r[0] = null;
|
|
||||||
l.countDown();
|
|
||||||
};
|
|
||||||
|
|
||||||
if (ThreePidMedium.Email.is(medium)) {
|
|
||||||
log.info("Performing E-mail 3PID lookup for {}", address);
|
|
||||||
getFirebase().getUserByEmail(address)
|
|
||||||
.addOnSuccessListener(success)
|
|
||||||
.addOnFailureListener(failure);
|
|
||||||
waitOnLatch(l);
|
|
||||||
} else if (ThreePidMedium.PhoneNumber.is(medium)) {
|
|
||||||
log.info("Performing msisdn 3PID lookup for {}", address);
|
|
||||||
getFirebase().getUserByPhoneNumber(address)
|
|
||||||
.addOnSuccessListener(success)
|
|
||||||
.addOnFailureListener(failure);
|
|
||||||
waitOnLatch(l);
|
|
||||||
} else {
|
|
||||||
log.info("{} is not a supported 3PID medium", medium);
|
|
||||||
r[0] = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Optional.ofNullable(r[0]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<SingleLookupReply> find(SingleLookupRequest request) {
|
public Optional<SingleLookupReply> find(SingleLookupRequest request) {
|
||||||
Optional<UserRecord> urOpt = findInternal(request.getType(), request.getThreePid());
|
Optional<String> uidOpt = findInternal(request.getType(), request.getThreePid());
|
||||||
return urOpt.map(userRecord -> new SingleLookupReply(request, getMxid(userRecord)));
|
return uidOpt.map(uid -> new SingleLookupReply(request, getMxid(uid)));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<ThreePidMapping> populate(List<ThreePidMapping> mappings) {
|
public List<ThreePidMapping> populate(List<ThreePidMapping> mappings) {
|
||||||
List<ThreePidMapping> results = new ArrayList<>();
|
List<ThreePidMapping> results = new ArrayList<>();
|
||||||
mappings.parallelStream().forEach(o -> {
|
mappings.forEach(o -> {
|
||||||
Optional<UserRecord> urOpt = findInternal(o.getMedium(), o.getValue());
|
Optional<String> uidOpt = findInternal(o.getMedium(), o.getValue());
|
||||||
if (urOpt.isPresent()) {
|
uidOpt.ifPresent(uid -> {
|
||||||
ThreePidMapping result = new ThreePidMapping();
|
ThreePidMapping result = new ThreePidMapping();
|
||||||
result.setMedium(o.getMedium());
|
result.setMedium(o.getMedium());
|
||||||
result.setValue(o.getValue());
|
result.setValue(o.getValue());
|
||||||
result.setMxid(getMxid(urOpt.get()));
|
result.setMxid(getMxid(uid));
|
||||||
results.add(result);
|
results.add(result);
|
||||||
}
|
});
|
||||||
});
|
});
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -32,7 +32,7 @@ import io.kamax.mxisd.config.MatrixConfig;
|
|||||||
import io.kamax.mxisd.config.ldap.LdapConfig;
|
import io.kamax.mxisd.config.ldap.LdapConfig;
|
||||||
import io.kamax.mxisd.exception.InternalServerError;
|
import io.kamax.mxisd.exception.InternalServerError;
|
||||||
import io.kamax.mxisd.util.GsonUtil;
|
import io.kamax.mxisd.util.GsonUtil;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.directory.api.ldap.model.cursor.CursorException;
|
import org.apache.directory.api.ldap.model.cursor.CursorException;
|
||||||
import org.apache.directory.api.ldap.model.cursor.CursorLdapReferralException;
|
import org.apache.directory.api.ldap.model.cursor.CursorLdapReferralException;
|
||||||
import org.apache.directory.api.ldap.model.cursor.EntryCursor;
|
import org.apache.directory.api.ldap.model.cursor.EntryCursor;
|
||||||
@@ -54,6 +54,8 @@ public class LdapAuthProvider extends LdapBackend implements AuthenticatorProvid
|
|||||||
|
|
||||||
private transient final Logger log = LoggerFactory.getLogger(LdapAuthProvider.class);
|
private transient final Logger log = LoggerFactory.getLogger(LdapAuthProvider.class);
|
||||||
|
|
||||||
|
public static final char[] CHARACTERS_TO_ESCAPE = ",#+<>;\"=*\\\\".toCharArray();
|
||||||
|
|
||||||
private PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
|
private PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
|
||||||
|
|
||||||
public LdapAuthProvider(LdapConfig cfg, MatrixConfig mxCfg) {
|
public LdapAuthProvider(LdapConfig cfg, MatrixConfig mxCfg) {
|
||||||
@@ -94,7 +96,8 @@ public class LdapAuthProvider extends LdapBackend implements AuthenticatorProvid
|
|||||||
return BackendAuthResult.failure();
|
return BackendAuthResult.failure();
|
||||||
}
|
}
|
||||||
|
|
||||||
String userFilter = "(" + getUidAtt() + "=" + userFilterValue + ")";
|
String filteredValue = escape(userFilterValue);
|
||||||
|
String userFilter = "(" + getUidAtt() + "=" + filteredValue + ")";
|
||||||
userFilter = buildWithFilter(userFilter, getCfg().getAuth().getFilter());
|
userFilter = buildWithFilter(userFilter, getCfg().getAuth().getFilter());
|
||||||
|
|
||||||
Set<String> attributes = new HashSet<>();
|
Set<String> attributes = new HashSet<>();
|
||||||
@@ -162,8 +165,21 @@ public class LdapAuthProvider extends LdapBackend implements AuthenticatorProvid
|
|||||||
log.info("No match were found for {}", mxid);
|
log.info("No match were found for {}", mxid);
|
||||||
return BackendAuthResult.failure();
|
return BackendAuthResult.failure();
|
||||||
} catch (LdapException | IOException | CursorException e) {
|
} catch (LdapException | IOException | CursorException e) {
|
||||||
|
log.error("Unable to invoke query request: ", e);
|
||||||
throw new InternalServerError(e);
|
throw new InternalServerError(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String escape(String raw) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
boolean escape;
|
||||||
|
for (char c : raw.toCharArray()) {
|
||||||
|
escape = false;
|
||||||
|
for (int i = 0; i < CHARACTERS_TO_ESCAPE.length && !escape; i++) {
|
||||||
|
escape = CHARACTERS_TO_ESCAPE[i] == c;
|
||||||
|
}
|
||||||
|
sb.append(escape ? "\\" + c : c);
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -20,10 +20,11 @@
|
|||||||
|
|
||||||
package io.kamax.mxisd.backend.ldap;
|
package io.kamax.mxisd.backend.ldap;
|
||||||
|
|
||||||
|
import io.kamax.matrix.MatrixID;
|
||||||
import io.kamax.matrix._MatrixID;
|
import io.kamax.matrix._MatrixID;
|
||||||
import io.kamax.mxisd.config.MatrixConfig;
|
import io.kamax.mxisd.config.MatrixConfig;
|
||||||
import io.kamax.mxisd.config.ldap.LdapConfig;
|
import io.kamax.mxisd.config.ldap.LdapConfig;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.directory.api.ldap.model.entry.Attribute;
|
import org.apache.directory.api.ldap.model.entry.Attribute;
|
||||||
import org.apache.directory.api.ldap.model.entry.AttributeUtils;
|
import org.apache.directory.api.ldap.model.entry.AttributeUtils;
|
||||||
import org.apache.directory.api.ldap.model.entry.Entry;
|
import org.apache.directory.api.ldap.model.entry.Entry;
|
||||||
@@ -116,10 +117,20 @@ public abstract class LdapBackend {
|
|||||||
|
|
||||||
public String buildMatrixIdFromUid(String uid) {
|
public String buildMatrixIdFromUid(String uid) {
|
||||||
String uidType = getCfg().getAttribute().getUid().getType();
|
String uidType = getCfg().getAttribute().getUid().getType();
|
||||||
|
String localpart = uid.toLowerCase();
|
||||||
|
|
||||||
|
if (!StringUtils.equals(uid, localpart)) {
|
||||||
|
log.info("UID {} from LDAP has been changed to lowercase to match the Synapse specifications", uid);
|
||||||
|
}
|
||||||
|
|
||||||
if (StringUtils.equals(UID, uidType)) {
|
if (StringUtils.equals(UID, uidType)) {
|
||||||
return "@" + uid + ":" + mxCfg.getDomain();
|
if(getCfg().isActiveDirectory()) {
|
||||||
|
localpart = new UPN(uid.toLowerCase()).getMXID();
|
||||||
|
}
|
||||||
|
|
||||||
|
return "@" + localpart + ":" + mxCfg.getDomain();
|
||||||
} else if (StringUtils.equals(MATRIX_ID, uidType)) {
|
} else if (StringUtils.equals(MATRIX_ID, uidType)) {
|
||||||
return uid;
|
return localpart;
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalArgumentException("Bind type " + uidType + " is not supported");
|
throw new IllegalArgumentException("Bind type " + uidType + " is not supported");
|
||||||
}
|
}
|
||||||
@@ -128,6 +139,10 @@ public abstract class LdapBackend {
|
|||||||
public String buildUidFromMatrixId(_MatrixID mxId) {
|
public String buildUidFromMatrixId(_MatrixID mxId) {
|
||||||
String uidType = getCfg().getAttribute().getUid().getType();
|
String uidType = getCfg().getAttribute().getUid().getType();
|
||||||
if (StringUtils.equals(UID, uidType)) {
|
if (StringUtils.equals(UID, uidType)) {
|
||||||
|
if(getCfg().isActiveDirectory()) {
|
||||||
|
return new UPN(mxId).getUPN();
|
||||||
|
}
|
||||||
|
|
||||||
return mxId.getLocalPart();
|
return mxId.getLocalPart();
|
||||||
} else if (StringUtils.equals(MATRIX_ID, uidType)) {
|
} else if (StringUtils.equals(MATRIX_ID, uidType)) {
|
||||||
return mxId.getId();
|
return mxId.getId();
|
||||||
@@ -169,4 +184,58 @@ public abstract class LdapBackend {
|
|||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class UPN {
|
||||||
|
private String login;
|
||||||
|
private String domain;
|
||||||
|
|
||||||
|
public UPN(String userPrincipalName) {
|
||||||
|
String[] uidParts = userPrincipalName.split("@");
|
||||||
|
|
||||||
|
if (uidParts.length != 2) {
|
||||||
|
throw new IllegalArgumentException(String.format("Wrong userPrincipalName provided: %s", userPrincipalName));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.login = uidParts[0];
|
||||||
|
this.domain = uidParts[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
public UPN(_MatrixID mxid) {
|
||||||
|
String[] idParts = mxid.getLocalPart().split("/");
|
||||||
|
|
||||||
|
if (idParts.length != 2) {
|
||||||
|
if(idParts.length == 1 && !StringUtils.isEmpty(getCfg().getDefaultDomain())) {
|
||||||
|
throw new IllegalArgumentException(String.format(
|
||||||
|
"Local part of mxid %s does not contains domain separator and default domain is not configured",
|
||||||
|
mxid.getLocalPart()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.domain = getCfg().getDefaultDomain();
|
||||||
|
} else {
|
||||||
|
this.domain = idParts[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
this.login = idParts[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLogin() {
|
||||||
|
return login;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDomain() {
|
||||||
|
return domain;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMXID() {
|
||||||
|
if(StringUtils.equalsIgnoreCase(getCfg().getDefaultDomain(), this.domain)) {
|
||||||
|
return this.login;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new StringBuilder(this.login).append("/").append(this.domain).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUPN() {
|
||||||
|
return new StringBuilder(this.login).append("@").append(this.domain).toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -41,7 +41,10 @@ import org.slf4j.LoggerFactory;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class LdapThreePidProvider extends LdapBackend implements IThreePidProvider {
|
public class LdapThreePidProvider extends LdapBackend implements IThreePidProvider {
|
||||||
|
|
||||||
@@ -137,4 +140,65 @@ public class LdapThreePidProvider extends LdapBackend implements IThreePidProvid
|
|||||||
return mappingsFound;
|
return mappingsFound;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<String> getAttributes() {
|
||||||
|
final List<String> attributes = getCfg().getAttribute().getThreepid().values().stream().flatMap(List::stream)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
attributes.add(getUidAtt());
|
||||||
|
return attributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<String> getAttributeValue(Entry entry, List<String> attributes) {
|
||||||
|
return attributes.stream()
|
||||||
|
.map(attr -> getAttribute(entry, attr))
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.filter(Optional::isPresent)
|
||||||
|
.map(Optional::get)
|
||||||
|
.findFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterable<ThreePidMapping> populateHashes() {
|
||||||
|
List<ThreePidMapping> result = new ArrayList<>();
|
||||||
|
if (!getCfg().getIdentity().isLookup()) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
String filter = getCfg().getIdentity().getFilter();
|
||||||
|
|
||||||
|
try (LdapConnection conn = getConn()) {
|
||||||
|
bind(conn);
|
||||||
|
|
||||||
|
log.debug("Query: {}", filter);
|
||||||
|
List<String> attributes = getAttributes();
|
||||||
|
log.debug("Attributes: {}", GsonUtil.build().toJson(attributes));
|
||||||
|
|
||||||
|
for (String baseDN : getBaseDNs()) {
|
||||||
|
log.debug("Base DN: {}", baseDN);
|
||||||
|
|
||||||
|
try (EntryCursor cursor = conn.search(baseDN, filter, SearchScope.SUBTREE, attributes.toArray(new String[0]))) {
|
||||||
|
while (cursor.next()) {
|
||||||
|
Entry entry = cursor.get();
|
||||||
|
log.info("Found possible match, DN: {}", entry.getDn().getName());
|
||||||
|
Optional<String> mxid = getAttribute(entry, getUidAtt());
|
||||||
|
if (!mxid.isPresent()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Map.Entry<String, List<String>> attributeEntry : getCfg().getAttribute().getThreepid().entrySet()) {
|
||||||
|
String medium = attributeEntry.getKey();
|
||||||
|
getAttributeValue(entry, attributeEntry.getValue())
|
||||||
|
.ifPresent(s -> result.add(new ThreePidMapping(medium, s, buildMatrixIdFromUid(mxid.get()))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (CursorLdapReferralException e) {
|
||||||
|
log.warn("3PID is only available via referral, skipping", e);
|
||||||
|
} catch (IOException | LdapException | CursorException e) {
|
||||||
|
log.error("Unable to fetch 3PID mappings", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (LdapException | IOException e) {
|
||||||
|
log.error("Unable to fetch 3PID mappings", e);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -38,7 +38,7 @@ import io.kamax.mxisd.lookup.SingleLookupRequest;
|
|||||||
import io.kamax.mxisd.lookup.ThreePidMapping;
|
import io.kamax.mxisd.lookup.ThreePidMapping;
|
||||||
import io.kamax.mxisd.lookup.provider.IThreePidProvider;
|
import io.kamax.mxisd.lookup.provider.IThreePidProvider;
|
||||||
import io.kamax.mxisd.profile.ProfileProvider;
|
import io.kamax.mxisd.profile.ProfileProvider;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
@@ -27,7 +27,7 @@ import io.kamax.mxisd.lookup.SingleLookupReply;
|
|||||||
import io.kamax.mxisd.lookup.SingleLookupRequest;
|
import io.kamax.mxisd.lookup.SingleLookupRequest;
|
||||||
import io.kamax.mxisd.lookup.ThreePidMapping;
|
import io.kamax.mxisd.lookup.ThreePidMapping;
|
||||||
import io.kamax.mxisd.lookup.provider.IThreePidProvider;
|
import io.kamax.mxisd.lookup.provider.IThreePidProvider;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@@ -107,14 +107,16 @@ public abstract class SqlThreePidProvider implements IThreePidProvider {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Iterable<ThreePidMapping> populateHashes() {
|
public Iterable<ThreePidMapping> populateHashes() {
|
||||||
if (StringUtils.isBlank(cfg.getLookup().getQuery())) {
|
String query = cfg.getLookup().getQuery();
|
||||||
|
if (StringUtils.isBlank(query)) {
|
||||||
log.warn("Lookup query not configured, skip.");
|
log.warn("Lookup query not configured, skip.");
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.debug("Uses query to match users: {}", query);
|
||||||
List<ThreePidMapping> result = new ArrayList<>();
|
List<ThreePidMapping> result = new ArrayList<>();
|
||||||
try (Connection connection = pool.get()) {
|
try (Connection connection = pool.get()) {
|
||||||
PreparedStatement statement = connection.prepareStatement(cfg.getLookup().getQuery());
|
PreparedStatement statement = connection.prepareStatement(query);
|
||||||
try (ResultSet resultSet = statement.executeQuery()) {
|
try (ResultSet resultSet = statement.executeQuery()) {
|
||||||
while (resultSet.next()) {
|
while (resultSet.next()) {
|
||||||
String mxid = resultSet.getString("mxid");
|
String mxid = resultSet.getString("mxid");
|
||||||
|
@@ -28,7 +28,7 @@ import io.kamax.mxisd.config.sql.generic.GenericSqlProviderConfig;
|
|||||||
import io.kamax.mxisd.directory.DirectoryProvider;
|
import io.kamax.mxisd.directory.DirectoryProvider;
|
||||||
import io.kamax.mxisd.exception.InternalServerError;
|
import io.kamax.mxisd.exception.InternalServerError;
|
||||||
import io.kamax.mxisd.http.io.UserDirectorySearchResult;
|
import io.kamax.mxisd.http.io.UserDirectorySearchResult;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
@@ -29,23 +29,27 @@ import java.util.Optional;
|
|||||||
|
|
||||||
public class Synapse {
|
public class Synapse {
|
||||||
|
|
||||||
private SqlConnectionPool pool;
|
private final SqlConnectionPool pool;
|
||||||
|
private final SynapseSqlProviderConfig providerConfig;
|
||||||
|
|
||||||
public Synapse(SynapseSqlProviderConfig sqlCfg) {
|
public Synapse(SynapseSqlProviderConfig sqlCfg) {
|
||||||
this.pool = new SqlConnectionPool(sqlCfg);
|
this.pool = new SqlConnectionPool(sqlCfg);
|
||||||
|
providerConfig = sqlCfg;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<String> getRoomName(String id) {
|
public Optional<String> getRoomName(String id) {
|
||||||
return pool.withConnFunction(conn -> {
|
String query = providerConfig.isLegacyRoomNames() ? SynapseQueries.getLegacyRoomName() : SynapseQueries.getRoomName();
|
||||||
PreparedStatement stmt = conn.prepareStatement(SynapseQueries.getRoomName());
|
|
||||||
stmt.setString(1, id);
|
|
||||||
ResultSet rSet = stmt.executeQuery();
|
|
||||||
if (!rSet.next()) {
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
return Optional.ofNullable(rSet.getString(1));
|
return pool.withConnFunction(conn -> {
|
||||||
|
try (PreparedStatement stmt = conn.prepareStatement(query)) {
|
||||||
|
stmt.setString(1, id);
|
||||||
|
ResultSet rSet = stmt.executeQuery();
|
||||||
|
if (!rSet.next()) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Optional.ofNullable(rSet.getString(1));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -21,7 +21,7 @@
|
|||||||
package io.kamax.mxisd.backend.sql.synapse;
|
package io.kamax.mxisd.backend.sql.synapse;
|
||||||
|
|
||||||
import io.kamax.mxisd.exception.ConfigurationException;
|
import io.kamax.mxisd.exception.ConfigurationException;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
public class SynapseQueries {
|
public class SynapseQueries {
|
||||||
|
|
||||||
@@ -51,7 +51,7 @@ public class SynapseQueries {
|
|||||||
if (StringUtils.equals("sqlite", type)) {
|
if (StringUtils.equals("sqlite", type)) {
|
||||||
return "select " + getUserId(type, domain) + ", displayname from profiles p where displayname like ?";
|
return "select " + getUserId(type, domain) + ", displayname from profiles p where displayname like ?";
|
||||||
} else if (StringUtils.equals("postgresql", type)) {
|
} else if (StringUtils.equals("postgresql", type)) {
|
||||||
return "select " + getUserId(type, domain) + ", displayname from profiles p where displayname ilike ?";
|
return "SELECT u.name,p.displayname FROM users u JOIN profiles p ON u.name LIKE concat('@',p.user_id,':%') WHERE u.is_guest = 0 AND u.appservice_id IS NULL AND p.displayname LIKE ?";
|
||||||
} else {
|
} else {
|
||||||
throw new ConfigurationException("Invalid Synapse SQL type: " + type);
|
throw new ConfigurationException("Invalid Synapse SQL type: " + type);
|
||||||
}
|
}
|
||||||
@@ -72,7 +72,10 @@ public class SynapseQueries {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static String getRoomName() {
|
public static String getRoomName() {
|
||||||
return "select r.name from room_names r, events e, (select r1.room_id,max(e1.origin_server_ts) ts from room_names r1, events e1 where r1.event_id = e1.event_id group by r1.room_id) rle where e.origin_server_ts = rle.ts and r.event_id = e.event_id and r.room_id = ?";
|
return "select name from room_stats_state where room_id = ? limit 1";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getLegacyRoomName() {
|
||||||
|
return "select r.name from room_names r, events e, (select r1.room_id,max(e1.origin_server_ts) ts from room_names r1, events e1 where r1.event_id = e1.event_id group by r1.room_id) rle where e.origin_server_ts = rle.ts and r.event_id = e.event_id and r.room_id = ?";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -25,7 +25,7 @@ import io.kamax.matrix._MatrixID;
|
|||||||
import io.kamax.mxisd.UserIdType;
|
import io.kamax.mxisd.UserIdType;
|
||||||
import io.kamax.mxisd.auth.provider.AuthenticatorProvider;
|
import io.kamax.mxisd.auth.provider.AuthenticatorProvider;
|
||||||
import io.kamax.mxisd.auth.provider.BackendAuthResult;
|
import io.kamax.mxisd.auth.provider.BackendAuthResult;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
@@ -25,7 +25,7 @@ import io.kamax.matrix.json.GsonUtil;
|
|||||||
import io.kamax.matrix.json.InvalidJsonException;
|
import io.kamax.matrix.json.InvalidJsonException;
|
||||||
import io.kamax.mxisd.config.wordpress.WordpressConfig;
|
import io.kamax.mxisd.config.wordpress.WordpressConfig;
|
||||||
import io.kamax.mxisd.util.RestClientUtils;
|
import io.kamax.mxisd.util.RestClientUtils;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||||
import org.apache.http.client.methods.HttpGet;
|
import org.apache.http.client.methods.HttpGet;
|
||||||
import org.apache.http.client.methods.HttpPost;
|
import org.apache.http.client.methods.HttpPost;
|
||||||
|
@@ -0,0 +1,5 @@
|
|||||||
|
package io.kamax.mxisd.config;
|
||||||
|
|
||||||
|
public interface DatabaseStorageConfig {
|
||||||
|
String getDatabase();
|
||||||
|
}
|
@@ -5,6 +5,7 @@ import org.slf4j.LoggerFactory;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
public class HashingConfig {
|
public class HashingConfig {
|
||||||
|
|
||||||
@@ -13,18 +14,19 @@ public class HashingConfig {
|
|||||||
private boolean enabled = false;
|
private boolean enabled = false;
|
||||||
private int pepperLength = 20;
|
private int pepperLength = 20;
|
||||||
private RotationPolicyEnum rotationPolicy;
|
private RotationPolicyEnum rotationPolicy;
|
||||||
private HashStorageEnum hashStorageType;
|
private HashStorageEnum hashStorageType = HashStorageEnum.in_memory;
|
||||||
private String delay = "10s";
|
private String delay = "10s";
|
||||||
private transient long delayInSeconds = 10;
|
private transient long delayInSeconds = 10;
|
||||||
private int requests = 10;
|
private int requests = 10;
|
||||||
private List<Algorithm> algorithms = new ArrayList<>();
|
private List<Algorithm> algorithms = new ArrayList<>();
|
||||||
|
|
||||||
public void build() {
|
public void build(MatrixConfig matrixConfig) {
|
||||||
if (isEnabled()) {
|
if (isEnabled()) {
|
||||||
LOGGER.info("--- Hash configuration ---");
|
LOGGER.info("--- Hash configuration ---");
|
||||||
LOGGER.info(" Pepper length: {}", getPepperLength());
|
LOGGER.info(" Pepper length: {}", getPepperLength());
|
||||||
LOGGER.info(" Rotation policy: {}", getRotationPolicy());
|
LOGGER.info(" Rotation policy: {}", getRotationPolicy());
|
||||||
LOGGER.info(" Hash storage type: {}", getHashStorageType());
|
LOGGER.info(" Hash storage type: {}", getHashStorageType());
|
||||||
|
Objects.requireNonNull(getHashStorageType(), "Storage type must be specified");
|
||||||
if (RotationPolicyEnum.per_seconds == getRotationPolicy()) {
|
if (RotationPolicyEnum.per_seconds == getRotationPolicy()) {
|
||||||
setDelayInSeconds(new DurationDeserializer().deserialize(getDelay()));
|
setDelayInSeconds(new DurationDeserializer().deserialize(getDelay()));
|
||||||
LOGGER.info(" Rotation delay: {}", getDelay());
|
LOGGER.info(" Rotation delay: {}", getDelay());
|
||||||
@@ -35,6 +37,9 @@ public class HashingConfig {
|
|||||||
}
|
}
|
||||||
LOGGER.info(" Algorithms: {}", getAlgorithms());
|
LOGGER.info(" Algorithms: {}", getAlgorithms());
|
||||||
} else {
|
} else {
|
||||||
|
if (matrixConfig.isV2()) {
|
||||||
|
LOGGER.warn("V2 enabled without the hash configuration.");
|
||||||
|
}
|
||||||
LOGGER.info("Hash configuration disabled, used only `none` pepper.");
|
LOGGER.info("Hash configuration disabled, used only `none` pepper.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
24
src/main/java/io/kamax/mxisd/config/InternalAPIConfig.java
Normal file
24
src/main/java/io/kamax/mxisd/config/InternalAPIConfig.java
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
package io.kamax.mxisd.config;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
public class InternalAPIConfig {
|
||||||
|
|
||||||
|
private final static Logger log = LoggerFactory.getLogger(InternalAPIConfig.class);
|
||||||
|
|
||||||
|
private boolean enabled = false;
|
||||||
|
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnabled(boolean enabled) {
|
||||||
|
this.enabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void build() {
|
||||||
|
log.info("--- Internal API config ---");
|
||||||
|
log.info("Internal API enabled: {}", isEnabled());
|
||||||
|
}
|
||||||
|
}
|
@@ -67,6 +67,7 @@ public class InvitationConfig {
|
|||||||
|
|
||||||
private boolean recursive = true;
|
private boolean recursive = true;
|
||||||
private long timer = 5;
|
private long timer = 5;
|
||||||
|
private PeriodDimension period = PeriodDimension.minutes;
|
||||||
|
|
||||||
public boolean isRecursive() {
|
public boolean isRecursive() {
|
||||||
return recursive;
|
return recursive;
|
||||||
@@ -84,6 +85,13 @@ public class InvitationConfig {
|
|||||||
this.timer = timer;
|
this.timer = timer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PeriodDimension getPeriod() {
|
||||||
|
return period;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPeriod(PeriodDimension period) {
|
||||||
|
this.period = period;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class SenderPolicy {
|
public static class SenderPolicy {
|
||||||
@@ -115,6 +123,7 @@ public class InvitationConfig {
|
|||||||
private Expiration expiration = new Expiration();
|
private Expiration expiration = new Expiration();
|
||||||
private Resolution resolution = new Resolution();
|
private Resolution resolution = new Resolution();
|
||||||
private Policies policy = new Policies();
|
private Policies policy = new Policies();
|
||||||
|
private boolean fullDisplayName = false;
|
||||||
|
|
||||||
public Expiration getExpiration() {
|
public Expiration getExpiration() {
|
||||||
return expiration;
|
return expiration;
|
||||||
@@ -140,11 +149,26 @@ public class InvitationConfig {
|
|||||||
this.policy = policy;
|
this.policy = policy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isFullDisplayName() {
|
||||||
|
return fullDisplayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFullDisplayName(boolean fullDisplayName) {
|
||||||
|
this.fullDisplayName = fullDisplayName;
|
||||||
|
}
|
||||||
|
|
||||||
public void build() {
|
public void build() {
|
||||||
log.info("--- Invite config ---");
|
log.info("--- Invite config ---");
|
||||||
log.info("Expiration: {}", GsonUtil.get().toJson(getExpiration()));
|
log.info("Expiration: {}", GsonUtil.get().toJson(getExpiration()));
|
||||||
log.info("Resolution: {}", GsonUtil.get().toJson(getResolution()));
|
log.info("Resolution: {}", GsonUtil.get().toJson(getResolution()));
|
||||||
log.info("Policies: {}", GsonUtil.get().toJson(getPolicy()));
|
log.info("Policies: {}", GsonUtil.get().toJson(getPolicy()));
|
||||||
|
log.info("Print full display name on invitation: {}", isFullDisplayName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum PeriodDimension {
|
||||||
|
|
||||||
|
minutes,
|
||||||
|
|
||||||
|
seconds
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -21,7 +21,7 @@
|
|||||||
package io.kamax.mxisd.config;
|
package io.kamax.mxisd.config;
|
||||||
|
|
||||||
import io.kamax.mxisd.exception.ConfigurationException;
|
import io.kamax.mxisd.exception.ConfigurationException;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
public class KeyConfig {
|
public class KeyConfig {
|
||||||
|
|
||||||
|
60
src/main/java/io/kamax/mxisd/config/LoggingConfig.java
Normal file
60
src/main/java/io/kamax/mxisd/config/LoggingConfig.java
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
package io.kamax.mxisd.config;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
public class LoggingConfig {
|
||||||
|
|
||||||
|
private static final Logger LOGGER = LoggerFactory.getLogger("App");
|
||||||
|
|
||||||
|
private String root;
|
||||||
|
private String app;
|
||||||
|
private boolean requests = false;
|
||||||
|
|
||||||
|
public String getRoot() {
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRoot(String root) {
|
||||||
|
this.root = root;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getApp() {
|
||||||
|
return app;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setApp(String app) {
|
||||||
|
this.app = app;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRequests() {
|
||||||
|
return requests;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRequests(boolean requests) {
|
||||||
|
this.requests = requests;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void build() {
|
||||||
|
LOGGER.info("Logging config:");
|
||||||
|
if (StringUtils.isNotBlank(getRoot())) {
|
||||||
|
LOGGER.info(" Default log level: {}", getRoot());
|
||||||
|
System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", getRoot());
|
||||||
|
}
|
||||||
|
|
||||||
|
String appLevel = System.getProperty("org.slf4j.simpleLogger.log.io.kamax.mxisd");
|
||||||
|
if (StringUtils.isNotBlank(appLevel)) {
|
||||||
|
LOGGER.info(" Logging level set by environment: {}", appLevel);
|
||||||
|
} else if (StringUtils.isNotBlank(getApp())) {
|
||||||
|
System.setProperty("org.slf4j.simpleLogger.log.io.kamax.mxisd", getApp());
|
||||||
|
LOGGER.info(" Logging level set by the configuration: {}", getApp());
|
||||||
|
} else {
|
||||||
|
LOGGER.info(" Logging level hasn't set, use default");
|
||||||
|
}
|
||||||
|
LOGGER.info(" Log requests: {}", isRequests());
|
||||||
|
if (isRequests()) {
|
||||||
|
LOGGER.warn(" Request dumping enabled, use this only to debug purposes, don't use it in the production.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -22,7 +22,7 @@ package io.kamax.mxisd.config;
|
|||||||
|
|
||||||
import io.kamax.matrix.json.GsonUtil;
|
import io.kamax.matrix.json.GsonUtil;
|
||||||
import io.kamax.mxisd.exception.ConfigurationException;
|
import io.kamax.mxisd.exception.ConfigurationException;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@@ -64,7 +64,7 @@ public class MatrixConfig {
|
|||||||
private String domain;
|
private String domain;
|
||||||
private Identity identity = new Identity();
|
private Identity identity = new Identity();
|
||||||
private boolean v1 = true;
|
private boolean v1 = true;
|
||||||
private boolean v2 = true;
|
private boolean v2 = false;
|
||||||
|
|
||||||
public String getDomain() {
|
public String getDomain() {
|
||||||
return domain;
|
return domain;
|
||||||
|
@@ -29,7 +29,7 @@ import io.kamax.mxisd.config.sql.synapse.SynapseSqlProviderConfig;
|
|||||||
import io.kamax.mxisd.config.threepid.ThreePidConfig;
|
import io.kamax.mxisd.config.threepid.ThreePidConfig;
|
||||||
import io.kamax.mxisd.config.threepid.notification.NotificationConfig;
|
import io.kamax.mxisd.config.threepid.notification.NotificationConfig;
|
||||||
import io.kamax.mxisd.config.wordpress.WordpressConfig;
|
import io.kamax.mxisd.config.wordpress.WordpressConfig;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@@ -117,6 +117,8 @@ public class MxisdConfig {
|
|||||||
private WordpressConfig wordpress = new WordpressConfig();
|
private WordpressConfig wordpress = new WordpressConfig();
|
||||||
private PolicyConfig policy = new PolicyConfig();
|
private PolicyConfig policy = new PolicyConfig();
|
||||||
private HashingConfig hashing = new HashingConfig();
|
private HashingConfig hashing = new HashingConfig();
|
||||||
|
private LoggingConfig logging = new LoggingConfig();
|
||||||
|
private InternalAPIConfig internal = new InternalAPIConfig();
|
||||||
|
|
||||||
public AppServiceConfig getAppsvc() {
|
public AppServiceConfig getAppsvc() {
|
||||||
return appsvc;
|
return appsvc;
|
||||||
@@ -342,6 +344,14 @@ public class MxisdConfig {
|
|||||||
this.hashing = hashing;
|
this.hashing = hashing;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LoggingConfig getLogging() {
|
||||||
|
return logging;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLogging(LoggingConfig logging) {
|
||||||
|
this.logging = logging;
|
||||||
|
}
|
||||||
|
|
||||||
public MxisdConfig inMemory() {
|
public MxisdConfig inMemory() {
|
||||||
getKey().setPath(":memory:");
|
getKey().setPath(":memory:");
|
||||||
getStorage().getProvider().getSqlite().setDatabase(":memory:");
|
getStorage().getProvider().getSqlite().setDatabase(":memory:");
|
||||||
@@ -349,7 +359,17 @@ public class MxisdConfig {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public InternalAPIConfig getInternal() {
|
||||||
|
return internal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInternal(InternalAPIConfig internal) {
|
||||||
|
this.internal = internal;
|
||||||
|
}
|
||||||
|
|
||||||
public MxisdConfig build() {
|
public MxisdConfig build() {
|
||||||
|
getLogging().build();
|
||||||
|
|
||||||
if (StringUtils.isBlank(getServer().getName())) {
|
if (StringUtils.isBlank(getServer().getName())) {
|
||||||
getServer().setName(getMatrix().getDomain());
|
getServer().setName(getMatrix().getDomain());
|
||||||
log.debug("server.name is empty, using matrix.domain");
|
log.debug("server.name is empty, using matrix.domain");
|
||||||
@@ -359,6 +379,7 @@ public class MxisdConfig {
|
|||||||
getAuth().build();
|
getAuth().build();
|
||||||
getAccountConfig().build();
|
getAccountConfig().build();
|
||||||
getDirectory().build();
|
getDirectory().build();
|
||||||
|
getDns().build();
|
||||||
getExec().build();
|
getExec().build();
|
||||||
getFirebase().build();
|
getFirebase().build();
|
||||||
getForward().build();
|
getForward().build();
|
||||||
@@ -381,7 +402,8 @@ public class MxisdConfig {
|
|||||||
getView().build();
|
getView().build();
|
||||||
getWordpress().build();
|
getWordpress().build();
|
||||||
getPolicy().build();
|
getPolicy().build();
|
||||||
getHashing().build();
|
getHashing().build(getMatrix());
|
||||||
|
getInternal().build();
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
105
src/main/java/io/kamax/mxisd/config/PostgresqlStorageConfig.java
Normal file
105
src/main/java/io/kamax/mxisd/config/PostgresqlStorageConfig.java
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
/*
|
||||||
|
* mxisd - Matrix Identity Server Daemon
|
||||||
|
* 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.mxisd.config;
|
||||||
|
|
||||||
|
public class PostgresqlStorageConfig implements DatabaseStorageConfig {
|
||||||
|
|
||||||
|
private String database;
|
||||||
|
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
private boolean pool;
|
||||||
|
|
||||||
|
private int maxConnectionsFree = 1;
|
||||||
|
|
||||||
|
private long maxConnectionAgeMillis = 60 * 60 * 1000;
|
||||||
|
|
||||||
|
private long checkConnectionsEveryMillis = 30 * 1000;
|
||||||
|
|
||||||
|
private boolean testBeforeGetFromPool = false;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDatabase() {
|
||||||
|
return database;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDatabase(String database) {
|
||||||
|
this.database = database;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUsername() {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUsername(String username) {
|
||||||
|
this.username = username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPassword() {
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPassword(String password) {
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPool() {
|
||||||
|
return pool;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPool(boolean pool) {
|
||||||
|
this.pool = pool;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMaxConnectionsFree() {
|
||||||
|
return maxConnectionsFree;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMaxConnectionsFree(int maxConnectionsFree) {
|
||||||
|
this.maxConnectionsFree = maxConnectionsFree;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getMaxConnectionAgeMillis() {
|
||||||
|
return maxConnectionAgeMillis;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMaxConnectionAgeMillis(long maxConnectionAgeMillis) {
|
||||||
|
this.maxConnectionAgeMillis = maxConnectionAgeMillis;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getCheckConnectionsEveryMillis() {
|
||||||
|
return checkConnectionsEveryMillis;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCheckConnectionsEveryMillis(long checkConnectionsEveryMillis) {
|
||||||
|
this.checkConnectionsEveryMillis = checkConnectionsEveryMillis;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isTestBeforeGetFromPool() {
|
||||||
|
return testBeforeGetFromPool;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTestBeforeGetFromPool(boolean testBeforeGetFromPool) {
|
||||||
|
this.testBeforeGetFromPool = testBeforeGetFromPool;
|
||||||
|
}
|
||||||
|
}
|
@@ -20,10 +20,11 @@
|
|||||||
|
|
||||||
package io.kamax.mxisd.config;
|
package io.kamax.mxisd.config;
|
||||||
|
|
||||||
public class SQLiteStorageConfig {
|
public class SQLiteStorageConfig implements DatabaseStorageConfig {
|
||||||
|
|
||||||
private String database;
|
private String database;
|
||||||
|
|
||||||
|
@Override
|
||||||
public String getDatabase() {
|
public String getDatabase() {
|
||||||
return database;
|
return database;
|
||||||
}
|
}
|
||||||
|
@@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
package io.kamax.mxisd.config;
|
package io.kamax.mxisd.config;
|
||||||
|
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@@ -34,6 +34,7 @@ public class ServerConfig {
|
|||||||
private String name;
|
private String name;
|
||||||
private int port = 8090;
|
private int port = 8090;
|
||||||
private String publicUrl;
|
private String publicUrl;
|
||||||
|
private String hostname;
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return name;
|
return name;
|
||||||
@@ -59,6 +60,14 @@ public class ServerConfig {
|
|||||||
this.publicUrl = publicUrl;
|
this.publicUrl = publicUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getHostname() {
|
||||||
|
return hostname;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHostname(String hostname) {
|
||||||
|
this.hostname = hostname;
|
||||||
|
}
|
||||||
|
|
||||||
public void build() {
|
public void build() {
|
||||||
log.info("--- Server config ---");
|
log.info("--- Server config ---");
|
||||||
|
|
||||||
@@ -75,8 +84,13 @@ public class ServerConfig {
|
|||||||
log.warn("Public URL is not valid: {}", StringUtils.defaultIfBlank(e.getMessage(), "<no reason provided>"));
|
log.warn("Public URL is not valid: {}", StringUtils.defaultIfBlank(e.getMessage(), "<no reason provided>"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (StringUtils.isBlank(getHostname())) {
|
||||||
|
setHostname("0.0.0.0");
|
||||||
|
}
|
||||||
|
|
||||||
log.info("Name: {}", getName());
|
log.info("Name: {}", getName());
|
||||||
log.info("Port: {}", getPort());
|
log.info("Port: {}", getPort());
|
||||||
log.info("Public URL: {}", getPublicUrl());
|
log.info("Public URL: {}", getPublicUrl());
|
||||||
|
log.info("Hostname: {}", getHostname());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -21,14 +21,21 @@
|
|||||||
package io.kamax.mxisd.config;
|
package io.kamax.mxisd.config;
|
||||||
|
|
||||||
import io.kamax.mxisd.exception.ConfigurationException;
|
import io.kamax.mxisd.exception.ConfigurationException;
|
||||||
import org.apache.commons.lang.StringUtils;
|
|
||||||
|
|
||||||
public class StorageConfig {
|
public class StorageConfig {
|
||||||
|
|
||||||
|
public enum BackendEnum {
|
||||||
|
sqlite,
|
||||||
|
|
||||||
|
postgresql
|
||||||
|
}
|
||||||
|
|
||||||
public static class Provider {
|
public static class Provider {
|
||||||
|
|
||||||
private SQLiteStorageConfig sqlite = new SQLiteStorageConfig();
|
private SQLiteStorageConfig sqlite = new SQLiteStorageConfig();
|
||||||
|
|
||||||
|
private PostgresqlStorageConfig postgresql = new PostgresqlStorageConfig();
|
||||||
|
|
||||||
public SQLiteStorageConfig getSqlite() {
|
public SQLiteStorageConfig getSqlite() {
|
||||||
return sqlite;
|
return sqlite;
|
||||||
}
|
}
|
||||||
@@ -37,16 +44,23 @@ public class StorageConfig {
|
|||||||
this.sqlite = sqlite;
|
this.sqlite = sqlite;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PostgresqlStorageConfig getPostgresql() {
|
||||||
|
return postgresql;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPostgresql(PostgresqlStorageConfig postgresql) {
|
||||||
|
this.postgresql = postgresql;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String backend = "sqlite";
|
private BackendEnum backend = BackendEnum.sqlite; // or postgresql
|
||||||
private Provider provider = new Provider();
|
private Provider provider = new Provider();
|
||||||
|
|
||||||
public String getBackend() {
|
public BackendEnum getBackend() {
|
||||||
return backend;
|
return backend;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setBackend(String backend) {
|
public void setBackend(BackendEnum backend) {
|
||||||
this.backend = backend;
|
this.backend = backend;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,7 +73,7 @@ public class StorageConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void build() {
|
public void build() {
|
||||||
if (StringUtils.isBlank(getBackend())) {
|
if (getBackend() == null) {
|
||||||
throw new ConfigurationException("storage.backend");
|
throw new ConfigurationException("storage.backend");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -21,7 +21,7 @@
|
|||||||
package io.kamax.mxisd.config;
|
package io.kamax.mxisd.config;
|
||||||
|
|
||||||
import io.kamax.matrix.json.GsonUtil;
|
import io.kamax.matrix.json.GsonUtil;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
@@ -1,79 +1,57 @@
|
|||||||
/*
|
|
||||||
* mxisd - Matrix Identity Server Daemon
|
|
||||||
* 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.mxisd.config;
|
package io.kamax.mxisd.config;
|
||||||
|
|
||||||
import io.kamax.matrix.json.GsonUtil;
|
|
||||||
import io.kamax.mxisd.exception.ConfigurationException;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.yaml.snakeyaml.Yaml;
|
import org.yaml.snakeyaml.Yaml;
|
||||||
import org.yaml.snakeyaml.constructor.Constructor;
|
import org.yaml.snakeyaml.constructor.Constructor;
|
||||||
import org.yaml.snakeyaml.introspector.BeanAccess;
|
import org.yaml.snakeyaml.LoaderOptions;
|
||||||
import org.yaml.snakeyaml.parser.ParserException;
|
|
||||||
import org.yaml.snakeyaml.representer.Representer;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileWriter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.File;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
public class YamlConfigLoader {
|
public class YamlConfigLoader {
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(YamlConfigLoader.class);
|
private static final Logger log = LoggerFactory.getLogger(YamlConfigLoader.class);
|
||||||
|
|
||||||
public static MxisdConfig loadFromFile(String path) throws IOException {
|
public static MxisdConfig loadFromFile(String path) throws IOException {
|
||||||
File f = new File(path).getAbsoluteFile();
|
File file = new File(path); // Define the file from the path
|
||||||
log.info("Reading config from {}", f.toString());
|
Constructor constructor = new Constructor(MxisdConfig.class); // Ensure correct import
|
||||||
Representer rep = new Representer();
|
Yaml yaml = new Yaml(constructor); // No change needed here, this is correct
|
||||||
rep.getPropertyUtils().setBeanAccess(BeanAccess.FIELD);
|
|
||||||
rep.getPropertyUtils().setAllowReadOnlyProperties(true);
|
|
||||||
rep.getPropertyUtils().setSkipMissingProperties(true);
|
|
||||||
Yaml yaml = new Yaml(new Constructor(MxisdConfig.class), rep);
|
|
||||||
try (FileInputStream is = new FileInputStream(f)) {
|
|
||||||
MxisdConfig raw = yaml.load(is);
|
|
||||||
log.debug("Read config in memory from {}", path);
|
|
||||||
|
|
||||||
// SnakeYaml set objects to null when there is no value set in the config, even a full sub-tree.
|
// Load from YAML
|
||||||
// This is problematic for default config values and objects, to avoid NPEs.
|
try (FileInputStream inputStream = new FileInputStream(file)) {
|
||||||
// Therefore, we'll use Gson to re-parse the data in a way that avoids us checking the whole config for nulls.
|
return yaml.loadAs(inputStream, MxisdConfig.class);
|
||||||
MxisdConfig cfg = GsonUtil.get().fromJson(GsonUtil.get().toJson(raw), MxisdConfig.class);
|
} catch (IOException e) {
|
||||||
|
// Handle exceptions
|
||||||
log.info("Loaded config from {}", path);
|
throw e;
|
||||||
return cfg;
|
|
||||||
} catch (ParserException t) {
|
|
||||||
throw new ConfigurationException(t.getMessage(), "Could not parse YAML config file - Please check indentation and that the configuration options exist");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static Optional<MxisdConfig> tryLoadFromFile(String path) {
|
public static Optional<MxisdConfig> tryLoadFromFile(String path) {
|
||||||
log.debug("Attempting to read config from {}", path);
|
|
||||||
try {
|
try {
|
||||||
return Optional.of(loadFromFile(path));
|
return Optional.of(loadFromFile(path));
|
||||||
} catch (FileNotFoundException e) {
|
|
||||||
log.info("No config file at {}", path);
|
|
||||||
return Optional.empty();
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException(e);
|
log.warn("Unable to load configuration file from path {}: {}", path, e.getMessage());
|
||||||
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void dumpConfig(MxisdConfig cfg, String outputPath) throws IOException {
|
||||||
|
// Initialize LoaderOptions for dumping if needed
|
||||||
|
LoaderOptions loaderOptions = new LoaderOptions();
|
||||||
|
// Customize loaderOptions as necessary
|
||||||
|
|
||||||
|
Yaml yaml = new Yaml(loaderOptions);
|
||||||
|
|
||||||
|
try (FileWriter writer = new FileWriter(new File(outputPath))) {
|
||||||
|
yaml.dump(cfg, writer);
|
||||||
|
log.info("Configuration dumped successfully to {}", outputPath);
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("Failed to dump YAML configuration to path: {}", outputPath, e);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -24,7 +24,7 @@ import io.kamax.matrix.ThreePidMedium;
|
|||||||
import io.kamax.matrix.json.GsonUtil;
|
import io.kamax.matrix.json.GsonUtil;
|
||||||
import io.kamax.mxisd.backend.ldap.LdapBackend;
|
import io.kamax.mxisd.backend.ldap.LdapBackend;
|
||||||
import io.kamax.mxisd.exception.ConfigurationException;
|
import io.kamax.mxisd.exception.ConfigurationException;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@@ -233,6 +233,7 @@ public abstract class LdapConfig {
|
|||||||
private String filter;
|
private String filter;
|
||||||
private String token = "%3pid";
|
private String token = "%3pid";
|
||||||
private Map<String, String> medium = new HashMap<>();
|
private Map<String, String> medium = new HashMap<>();
|
||||||
|
private boolean lookup = false;
|
||||||
|
|
||||||
public String getFilter() {
|
public String getFilter() {
|
||||||
return filter;
|
return filter;
|
||||||
@@ -262,6 +263,13 @@ public abstract class LdapConfig {
|
|||||||
this.medium = medium;
|
this.medium = medium;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isLookup() {
|
||||||
|
return lookup;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLookup(boolean lookup) {
|
||||||
|
this.lookup = lookup;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Profile {
|
public static class Profile {
|
||||||
@@ -283,6 +291,9 @@ public abstract class LdapConfig {
|
|||||||
private boolean enabled;
|
private boolean enabled;
|
||||||
private String filter;
|
private String filter;
|
||||||
|
|
||||||
|
private boolean activeDirectory;
|
||||||
|
private String defaultDomain;
|
||||||
|
|
||||||
private Connection connection = new Connection();
|
private Connection connection = new Connection();
|
||||||
private Attribute attribute = new Attribute();
|
private Attribute attribute = new Attribute();
|
||||||
private Auth auth = new Auth();
|
private Auth auth = new Auth();
|
||||||
@@ -308,6 +319,22 @@ public abstract class LdapConfig {
|
|||||||
this.filter = filter;
|
this.filter = filter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isActiveDirectory() {
|
||||||
|
return activeDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setActiveDirectory(boolean activeDirectory) {
|
||||||
|
this.activeDirectory = activeDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDefaultDomain() {
|
||||||
|
return defaultDomain;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDefaultDomain(String defaultDomain) {
|
||||||
|
this.defaultDomain = defaultDomain;
|
||||||
|
}
|
||||||
|
|
||||||
public Connection getConnection() {
|
public Connection getConnection() {
|
||||||
return connection;
|
return connection;
|
||||||
}
|
}
|
||||||
@@ -399,6 +426,15 @@ public abstract class LdapConfig {
|
|||||||
throw new ConfigurationException("ldap.identity.token");
|
throw new ConfigurationException("ldap.identity.token");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(isActiveDirectory()) {
|
||||||
|
if(!StringUtils.equals(LdapBackend.UID, uidType)) {
|
||||||
|
throw new IllegalArgumentException(String.format(
|
||||||
|
"Attribute UID type should be set to %s in Active Directory mode",
|
||||||
|
LdapBackend.UID
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Build queries
|
// Build queries
|
||||||
attribute.getThreepid().forEach((k, v) -> {
|
attribute.getThreepid().forEach((k, v) -> {
|
||||||
if (StringUtils.isBlank(identity.getMedium().get(k))) {
|
if (StringUtils.isBlank(identity.getMedium().get(k))) {
|
||||||
|
@@ -45,4 +45,21 @@ public class MemoryThreePid implements _ThreePid {
|
|||||||
this.address = address;
|
this.address = address;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
|
||||||
|
MemoryThreePid threePid = (MemoryThreePid) 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -21,7 +21,7 @@
|
|||||||
package io.kamax.mxisd.config.rest;
|
package io.kamax.mxisd.config.rest;
|
||||||
|
|
||||||
import io.kamax.mxisd.exception.ConfigurationException;
|
import io.kamax.mxisd.exception.ConfigurationException;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@@ -33,8 +33,8 @@ public class RestBackendConfig {
|
|||||||
|
|
||||||
public static class IdentityEndpoints {
|
public static class IdentityEndpoints {
|
||||||
|
|
||||||
private String single = "/_ma1sd/backend/api/v1/identity/single";
|
private String single = "/_mxids/backend/api/v1/identity/single";
|
||||||
private String bulk = "/_ma1sd/backend/api/v1/identity/bulk";
|
private String bulk = "/_mxids/backend/api/v1/identity/bulk";
|
||||||
|
|
||||||
public String getSingle() {
|
public String getSingle() {
|
||||||
return single;
|
return single;
|
||||||
@@ -56,9 +56,9 @@ public class RestBackendConfig {
|
|||||||
|
|
||||||
public static class ProfileEndpoints {
|
public static class ProfileEndpoints {
|
||||||
|
|
||||||
private String displayName = "/_ma1sd/backend/api/v1/profile/displayName";
|
private String displayName = "/_mxids/backend/api/v1/profile/displayName";
|
||||||
private String threepids = "/_ma1sd/backend/api/v1/profile/threepids";
|
private String threepids = "/_mxids/backend/api/v1/profile/threepids";
|
||||||
private String roles = "/_ma1sd/backend/api/v1/profile/roles";
|
private String roles = "/_mxids/backend/api/v1/profile/roles";
|
||||||
|
|
||||||
public String getDisplayName() {
|
public String getDisplayName() {
|
||||||
return displayName;
|
return displayName;
|
||||||
@@ -88,8 +88,8 @@ public class RestBackendConfig {
|
|||||||
|
|
||||||
public static class Endpoints {
|
public static class Endpoints {
|
||||||
|
|
||||||
private String auth = "/_ma1sd/backend/api/v1/auth/login";
|
private String auth = "/_mxids/backend/api/v1/auth/login";
|
||||||
private String directory = "/_ma1sd/backend/api/v1/directory/user/search";
|
private String directory = "/_mxids/backend/api/v1/directory/user/search";
|
||||||
private IdentityEndpoints identity = new IdentityEndpoints();
|
private IdentityEndpoints identity = new IdentityEndpoints();
|
||||||
private ProfileEndpoints profile = new ProfileEndpoints();
|
private ProfileEndpoints profile = new ProfileEndpoints();
|
||||||
|
|
||||||
|
@@ -125,7 +125,7 @@ public abstract class SqlConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static class Lookup {
|
public static class Lookup {
|
||||||
private String query = "SELECT user_id AS mxid, medium, address from user_threepids";
|
private String query = "SELECT user_id AS mxid, medium, address from user_threepid_id_server";
|
||||||
|
|
||||||
public String getQuery() {
|
public String getQuery() {
|
||||||
return query;
|
return query;
|
||||||
@@ -140,7 +140,7 @@ public abstract class SqlConfig {
|
|||||||
|
|
||||||
private Boolean enabled;
|
private Boolean enabled;
|
||||||
private String type = "mxid";
|
private String type = "mxid";
|
||||||
private String query = "SELECT user_id AS uid FROM user_threepids WHERE medium = ? AND address = ?";
|
private String query = "SELECT user_id AS uid FROM user_threepid_id_server WHERE medium = ? AND address = ?";
|
||||||
private Map<String, String> medium = new HashMap<>();
|
private Map<String, String> medium = new HashMap<>();
|
||||||
|
|
||||||
public Boolean isEnabled() {
|
public Boolean isEnabled() {
|
||||||
|
@@ -23,10 +23,24 @@ package io.kamax.mxisd.config.sql.synapse;
|
|||||||
import io.kamax.mxisd.UserIdType;
|
import io.kamax.mxisd.UserIdType;
|
||||||
import io.kamax.mxisd.backend.sql.synapse.SynapseQueries;
|
import io.kamax.mxisd.backend.sql.synapse.SynapseQueries;
|
||||||
import io.kamax.mxisd.config.sql.SqlConfig;
|
import io.kamax.mxisd.config.sql.SqlConfig;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
public class SynapseSqlProviderConfig extends SqlConfig {
|
public class SynapseSqlProviderConfig extends SqlConfig {
|
||||||
|
|
||||||
|
private transient final Logger log = LoggerFactory.getLogger(SynapseSqlProviderConfig.class);
|
||||||
|
|
||||||
|
private boolean legacyRoomNames = false;
|
||||||
|
|
||||||
|
public boolean isLegacyRoomNames() {
|
||||||
|
return legacyRoomNames;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLegacyRoomNames(boolean legacyRoomNames) {
|
||||||
|
this.legacyRoomNames = legacyRoomNames;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getProviderName() {
|
protected String getProviderName() {
|
||||||
return "Synapse SQL";
|
return "Synapse SQL";
|
||||||
@@ -42,7 +56,7 @@ public class SynapseSqlProviderConfig extends SqlConfig {
|
|||||||
|
|
||||||
if (getIdentity().isEnabled() && StringUtils.isBlank(getIdentity().getType())) {
|
if (getIdentity().isEnabled() && StringUtils.isBlank(getIdentity().getType())) {
|
||||||
getIdentity().setType("mxid");
|
getIdentity().setType("mxid");
|
||||||
getIdentity().setQuery("SELECT user_id AS uid FROM user_threepids WHERE medium = ? AND address = ?");
|
getIdentity().setQuery("SELECT user_id AS uid FROM user_threepid_id_server WHERE medium = ? AND address = ?");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getProfile().isEnabled()) {
|
if (getProfile().isEnabled()) {
|
||||||
@@ -65,4 +79,12 @@ public class SynapseSqlProviderConfig extends SqlConfig {
|
|||||||
printConfig();
|
printConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void printConfig() {
|
||||||
|
super.printConfig();
|
||||||
|
|
||||||
|
if (isEnabled()) {
|
||||||
|
log.info("Use legacy room name query: {}", isLegacyRoomNames());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -21,7 +21,7 @@
|
|||||||
package io.kamax.mxisd.config.threepid.connector;
|
package io.kamax.mxisd.config.threepid.connector;
|
||||||
|
|
||||||
import io.kamax.mxisd.util.GsonUtil;
|
import io.kamax.mxisd.util.GsonUtil;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
@@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
package io.kamax.mxisd.config.threepid.connector;
|
package io.kamax.mxisd.config.threepid.connector;
|
||||||
|
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
@@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
package io.kamax.mxisd.config.threepid.medium;
|
package io.kamax.mxisd.config.threepid.medium;
|
||||||
|
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@@ -21,7 +21,7 @@
|
|||||||
package io.kamax.mxisd.config.wordpress;
|
package io.kamax.mxisd.config.wordpress;
|
||||||
|
|
||||||
import io.kamax.mxisd.exception.ConfigurationException;
|
import io.kamax.mxisd.exception.ConfigurationException;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@@ -31,7 +31,7 @@ import io.kamax.mxisd.http.io.UserDirectorySearchRequest;
|
|||||||
import io.kamax.mxisd.http.io.UserDirectorySearchResult;
|
import io.kamax.mxisd.http.io.UserDirectorySearchResult;
|
||||||
import io.kamax.mxisd.util.RestClientUtils;
|
import io.kamax.mxisd.util.RestClientUtils;
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||||
import org.apache.http.client.methods.HttpPost;
|
import org.apache.http.client.methods.HttpPost;
|
||||||
import org.apache.http.client.utils.URIBuilder;
|
import org.apache.http.client.utils.URIBuilder;
|
||||||
|
@@ -23,5 +23,6 @@ package io.kamax.mxisd.exception;
|
|||||||
public class InvalidParamException extends RuntimeException {
|
public class InvalidParamException extends RuntimeException {
|
||||||
|
|
||||||
public InvalidParamException() {
|
public InvalidParamException() {
|
||||||
|
super("The chosen hash algorithm is invalid or disallowed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -23,5 +23,6 @@ package io.kamax.mxisd.exception;
|
|||||||
public class InvalidPepperException extends RuntimeException {
|
public class InvalidPepperException extends RuntimeException {
|
||||||
|
|
||||||
public InvalidPepperException() {
|
public InvalidPepperException() {
|
||||||
|
super("The provided pepper is invalid or expired");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* mxids - Matrix Identity Server Daemon
|
||||||
|
* Copyright (C) 2020 Anatoliy SAblin
|
||||||
|
*
|
||||||
|
* https://git.cqre.net/cqrenet/mxids/
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.kamax.mxisd.exception;
|
||||||
|
|
||||||
|
public class TermsNotSignedException extends RuntimeException {
|
||||||
|
|
||||||
|
public TermsNotSignedException() {
|
||||||
|
super("Please accept our updated terms of service before continuing");
|
||||||
|
}
|
||||||
|
}
|
@@ -1,6 +1,9 @@
|
|||||||
package io.kamax.mxisd.hash;
|
package io.kamax.mxisd.hash;
|
||||||
|
|
||||||
import io.kamax.mxisd.config.HashingConfig;
|
import io.kamax.mxisd.config.HashingConfig;
|
||||||
|
import io.kamax.mxisd.hash.engine.Engine;
|
||||||
|
import io.kamax.mxisd.hash.engine.HashEngine;
|
||||||
|
import io.kamax.mxisd.hash.engine.NoneEngine;
|
||||||
import io.kamax.mxisd.hash.rotation.HashRotationStrategy;
|
import io.kamax.mxisd.hash.rotation.HashRotationStrategy;
|
||||||
import io.kamax.mxisd.hash.rotation.NoOpRotationStrategy;
|
import io.kamax.mxisd.hash.rotation.NoOpRotationStrategy;
|
||||||
import io.kamax.mxisd.hash.rotation.RotationPerRequests;
|
import io.kamax.mxisd.hash.rotation.RotationPerRequests;
|
||||||
@@ -21,7 +24,7 @@ public class HashManager {
|
|||||||
|
|
||||||
private static final Logger LOGGER = LoggerFactory.getLogger(HashManager.class);
|
private static final Logger LOGGER = LoggerFactory.getLogger(HashManager.class);
|
||||||
|
|
||||||
private HashEngine hashEngine;
|
private Engine engine;
|
||||||
private HashRotationStrategy rotationStrategy;
|
private HashRotationStrategy rotationStrategy;
|
||||||
private HashStorage hashStorage;
|
private HashStorage hashStorage;
|
||||||
private HashingConfig config;
|
private HashingConfig config;
|
||||||
@@ -32,7 +35,7 @@ public class HashManager {
|
|||||||
this.config = config;
|
this.config = config;
|
||||||
this.storage = storage;
|
this.storage = storage;
|
||||||
initStorage();
|
initStorage();
|
||||||
hashEngine = new HashEngine(providers, getHashStorage(), config);
|
engine = config.isEnabled() ? new HashEngine(providers, getHashStorage(), config) : new NoneEngine();
|
||||||
initRotationStrategy();
|
initRotationStrategy();
|
||||||
configured.set(true);
|
configured.set(true);
|
||||||
}
|
}
|
||||||
@@ -73,8 +76,8 @@ public class HashManager {
|
|||||||
this.rotationStrategy.register(getHashEngine());
|
this.rotationStrategy.register(getHashEngine());
|
||||||
}
|
}
|
||||||
|
|
||||||
public HashEngine getHashEngine() {
|
public Engine getHashEngine() {
|
||||||
return hashEngine;
|
return engine;
|
||||||
}
|
}
|
||||||
|
|
||||||
public HashRotationStrategy getRotationStrategy() {
|
public HashRotationStrategy getRotationStrategy() {
|
||||||
|
7
src/main/java/io/kamax/mxisd/hash/engine/Engine.java
Normal file
7
src/main/java/io/kamax/mxisd/hash/engine/Engine.java
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package io.kamax.mxisd.hash.engine;
|
||||||
|
|
||||||
|
public interface Engine {
|
||||||
|
void updateHashes();
|
||||||
|
|
||||||
|
String getPepper();
|
||||||
|
}
|
@@ -1,4 +1,4 @@
|
|||||||
package io.kamax.mxisd.hash;
|
package io.kamax.mxisd.hash.engine;
|
||||||
|
|
||||||
import io.kamax.mxisd.config.HashingConfig;
|
import io.kamax.mxisd.config.HashingConfig;
|
||||||
import io.kamax.mxisd.hash.storage.HashStorage;
|
import io.kamax.mxisd.hash.storage.HashStorage;
|
||||||
@@ -12,7 +12,7 @@ import org.slf4j.LoggerFactory;
|
|||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class HashEngine {
|
public class HashEngine implements Engine {
|
||||||
|
|
||||||
private static final Logger LOGGER = LoggerFactory.getLogger(HashEngine.class);
|
private static final Logger LOGGER = LoggerFactory.getLogger(HashEngine.class);
|
||||||
|
|
||||||
@@ -28,6 +28,7 @@ public class HashEngine {
|
|||||||
this.config = config;
|
this.config = config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void updateHashes() {
|
public void updateHashes() {
|
||||||
LOGGER.info("Start update hashes.");
|
LOGGER.info("Start update hashes.");
|
||||||
synchronized (hashStorage) {
|
synchronized (hashStorage) {
|
||||||
@@ -35,7 +36,9 @@ public class HashEngine {
|
|||||||
hashStorage.clear();
|
hashStorage.clear();
|
||||||
for (IThreePidProvider provider : providers) {
|
for (IThreePidProvider provider : providers) {
|
||||||
try {
|
try {
|
||||||
|
LOGGER.info("Populate hashes from the handler: {}", provider.getClass().getCanonicalName());
|
||||||
for (ThreePidMapping pidMapping : provider.populateHashes()) {
|
for (ThreePidMapping pidMapping : provider.populateHashes()) {
|
||||||
|
LOGGER.debug("Found 3PID: {}", pidMapping);
|
||||||
hashStorage.add(pidMapping, hash(pidMapping));
|
hashStorage.add(pidMapping, hash(pidMapping));
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -46,6 +49,7 @@ public class HashEngine {
|
|||||||
LOGGER.info("Finish update hashes.");
|
LOGGER.info("Finish update hashes.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public String getPepper() {
|
public String getPepper() {
|
||||||
synchronized (hashStorage) {
|
synchronized (hashStorage) {
|
||||||
return pepper;
|
return pepper;
|
19
src/main/java/io/kamax/mxisd/hash/engine/NoneEngine.java
Normal file
19
src/main/java/io/kamax/mxisd/hash/engine/NoneEngine.java
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
package io.kamax.mxisd.hash.engine;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
public class NoneEngine implements Engine {
|
||||||
|
|
||||||
|
private static final Logger LOGGER = LoggerFactory.getLogger(NoneEngine.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateHashes() {
|
||||||
|
LOGGER.info("Nothing to update.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPepper() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
@@ -1,12 +1,12 @@
|
|||||||
package io.kamax.mxisd.hash.rotation;
|
package io.kamax.mxisd.hash.rotation;
|
||||||
|
|
||||||
import io.kamax.mxisd.hash.HashEngine;
|
import io.kamax.mxisd.hash.engine.Engine;
|
||||||
|
|
||||||
public interface HashRotationStrategy {
|
public interface HashRotationStrategy {
|
||||||
|
|
||||||
void register(HashEngine hashEngine);
|
void register(Engine engine);
|
||||||
|
|
||||||
HashEngine getHashEngine();
|
Engine getHashEngine();
|
||||||
|
|
||||||
void newRequest();
|
void newRequest();
|
||||||
|
|
||||||
|
@@ -1,19 +1,19 @@
|
|||||||
package io.kamax.mxisd.hash.rotation;
|
package io.kamax.mxisd.hash.rotation;
|
||||||
|
|
||||||
import io.kamax.mxisd.hash.HashEngine;
|
import io.kamax.mxisd.hash.engine.Engine;
|
||||||
|
|
||||||
public class NoOpRotationStrategy implements HashRotationStrategy {
|
public class NoOpRotationStrategy implements HashRotationStrategy {
|
||||||
|
|
||||||
private HashEngine hashEngine;
|
private Engine engine;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void register(HashEngine hashEngine) {
|
public void register(Engine engine) {
|
||||||
this.hashEngine = hashEngine;
|
this.engine = engine;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HashEngine getHashEngine() {
|
public Engine getHashEngine() {
|
||||||
return hashEngine;
|
return engine;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@@ -1,12 +1,12 @@
|
|||||||
package io.kamax.mxisd.hash.rotation;
|
package io.kamax.mxisd.hash.rotation;
|
||||||
|
|
||||||
import io.kamax.mxisd.hash.HashEngine;
|
import io.kamax.mxisd.hash.engine.Engine;
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
public class RotationPerRequests implements HashRotationStrategy {
|
public class RotationPerRequests implements HashRotationStrategy {
|
||||||
|
|
||||||
private HashEngine hashEngine;
|
private Engine engine;
|
||||||
private final AtomicInteger counter = new AtomicInteger(0);
|
private final AtomicInteger counter = new AtomicInteger(0);
|
||||||
private final int barrier;
|
private final int barrier;
|
||||||
|
|
||||||
@@ -15,14 +15,14 @@ public class RotationPerRequests implements HashRotationStrategy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void register(HashEngine hashEngine) {
|
public void register(Engine engine) {
|
||||||
this.hashEngine = hashEngine;
|
this.engine = engine;
|
||||||
trigger();
|
trigger();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HashEngine getHashEngine() {
|
public Engine getHashEngine() {
|
||||||
return hashEngine;
|
return engine;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
package io.kamax.mxisd.hash.rotation;
|
package io.kamax.mxisd.hash.rotation;
|
||||||
|
|
||||||
import io.kamax.mxisd.hash.HashEngine;
|
import io.kamax.mxisd.hash.engine.Engine;
|
||||||
|
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
@@ -9,7 +9,7 @@ import java.util.concurrent.TimeUnit;
|
|||||||
public class TimeBasedRotation implements HashRotationStrategy {
|
public class TimeBasedRotation implements HashRotationStrategy {
|
||||||
|
|
||||||
private final long delay;
|
private final long delay;
|
||||||
private HashEngine hashEngine;
|
private Engine engine;
|
||||||
private final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
|
private final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
|
||||||
|
|
||||||
public TimeBasedRotation(long delay) {
|
public TimeBasedRotation(long delay) {
|
||||||
@@ -17,15 +17,15 @@ public class TimeBasedRotation implements HashRotationStrategy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void register(HashEngine hashEngine) {
|
public void register(Engine engine) {
|
||||||
this.hashEngine = hashEngine;
|
this.engine = engine;
|
||||||
Runtime.getRuntime().addShutdownHook(new Thread(executorService::shutdown));
|
Runtime.getRuntime().addShutdownHook(new Thread(executorService::shutdown));
|
||||||
executorService.scheduleWithFixedDelay(this::trigger, 0, delay, TimeUnit.SECONDS);
|
executorService.scheduleWithFixedDelay(this::trigger, 0, delay, TimeUnit.SECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HashEngine getHashEngine() {
|
public Engine getHashEngine() {
|
||||||
return hashEngine;
|
return engine;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@@ -20,11 +20,19 @@
|
|||||||
|
|
||||||
package io.kamax.mxisd.http;
|
package io.kamax.mxisd.http;
|
||||||
|
|
||||||
|
import static io.kamax.mxisd.util.RestClientUtils.urlEncode;
|
||||||
|
|
||||||
public class IsAPIv1 {
|
public class IsAPIv1 {
|
||||||
|
|
||||||
public static final String Base = "/_matrix/identity/api/v1";
|
public static final String Base = "/_matrix/identity/api/v1";
|
||||||
|
|
||||||
public static String getValidate(String medium, String sid, String secret, String token) {
|
public static String getValidate(String medium, String sid, String secret, String token) {
|
||||||
return String.format("%s/validate/%s/submitToken?sid=%s&client_secret=%s&token=%s", Base, medium, sid, secret, token);
|
return String.format("%s/validate/%s/submitToken?sid=%s&client_secret=%s&token=%s",
|
||||||
|
Base,
|
||||||
|
medium,
|
||||||
|
urlEncode(sid),
|
||||||
|
urlEncode(secret),
|
||||||
|
urlEncode(token)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,5 @@
|
|||||||
|
package io.kamax.mxisd.http.undertow.conduit;
|
||||||
|
|
||||||
|
public interface ConduitWithDump {
|
||||||
|
String dump();
|
||||||
|
}
|
@@ -0,0 +1,107 @@
|
|||||||
|
/*
|
||||||
|
* JBoss, Home of Professional Open Source.
|
||||||
|
* Copyright 2014 Red Hat, Inc., and individual contributors
|
||||||
|
* as indicated by the @author tags.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.kamax.mxisd.http.undertow.conduit;
|
||||||
|
|
||||||
|
import org.xnio.IoUtils;
|
||||||
|
import org.xnio.channels.StreamSourceChannel;
|
||||||
|
import org.xnio.conduits.AbstractStreamSinkConduit;
|
||||||
|
import org.xnio.conduits.ConduitWritableByteChannel;
|
||||||
|
import org.xnio.conduits.Conduits;
|
||||||
|
import org.xnio.conduits.StreamSinkConduit;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.channels.FileChannel;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Conduit that saves all the data that is written through it and can dump it to the console
|
||||||
|
* <p>
|
||||||
|
* Obviously this should not be used in production.
|
||||||
|
*
|
||||||
|
* @author Stuart Douglas
|
||||||
|
*/
|
||||||
|
public class DebuggingStreamSinkConduit extends AbstractStreamSinkConduit<StreamSinkConduit> implements ConduitWithDump {
|
||||||
|
|
||||||
|
private final List<byte[]> data = new CopyOnWriteArrayList<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new instance.
|
||||||
|
*
|
||||||
|
* @param next the delegate conduit to set
|
||||||
|
*/
|
||||||
|
public DebuggingStreamSinkConduit(StreamSinkConduit next) {
|
||||||
|
super(next);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int write(ByteBuffer src) throws IOException {
|
||||||
|
int pos = src.position();
|
||||||
|
int res = super.write(src);
|
||||||
|
if (res > 0) {
|
||||||
|
byte[] d = new byte[res];
|
||||||
|
for (int i = 0; i < res; ++i) {
|
||||||
|
d[i] = src.get(i + pos);
|
||||||
|
}
|
||||||
|
data.add(d);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long write(ByteBuffer[] dsts, int offs, int len) throws IOException {
|
||||||
|
for (int i = offs; i < len; ++i) {
|
||||||
|
if (dsts[i].hasRemaining()) {
|
||||||
|
return write(dsts[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long transferFrom(final FileChannel src, final long position, final long count) throws IOException {
|
||||||
|
return src.transferTo(position, count, new ConduitWritableByteChannel(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long transferFrom(final StreamSourceChannel source, final long count, final ByteBuffer throughBuffer) throws IOException {
|
||||||
|
return IoUtils.transfer(source, count, throughBuffer, new ConduitWritableByteChannel(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int writeFinal(ByteBuffer src) throws IOException {
|
||||||
|
return Conduits.writeFinalBasic(this, src);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long writeFinal(ByteBuffer[] srcs, int offset, int length) throws IOException {
|
||||||
|
return Conduits.writeFinalBasic(this, srcs, offset, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String dump() {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (byte[] datum : data) {
|
||||||
|
sb.append(new String(datum, StandardCharsets.UTF_8));
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
* JBoss, Home of Professional Open Source.
|
||||||
|
* Copyright 2014 Red Hat, Inc., and individual contributors
|
||||||
|
* as indicated by the @author tags.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.kamax.mxisd.http.undertow.conduit;
|
||||||
|
|
||||||
|
import org.xnio.IoUtils;
|
||||||
|
import org.xnio.channels.StreamSinkChannel;
|
||||||
|
import org.xnio.conduits.AbstractStreamSourceConduit;
|
||||||
|
import org.xnio.conduits.ConduitReadableByteChannel;
|
||||||
|
import org.xnio.conduits.StreamSourceConduit;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.channels.FileChannel;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Conduit that saves all the data that is written through it and can dump it to the console
|
||||||
|
* <p>
|
||||||
|
* Obviously this should not be used in production.
|
||||||
|
*
|
||||||
|
* @author Stuart Douglas
|
||||||
|
*/
|
||||||
|
public class DebuggingStreamSourceConduit extends AbstractStreamSourceConduit<StreamSourceConduit> implements ConduitWithDump {
|
||||||
|
|
||||||
|
private final List<byte[]> data = new CopyOnWriteArrayList<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new instance.
|
||||||
|
*
|
||||||
|
* @param next the delegate conduit to set
|
||||||
|
*/
|
||||||
|
public DebuggingStreamSourceConduit(StreamSourceConduit next) {
|
||||||
|
super(next);
|
||||||
|
}
|
||||||
|
|
||||||
|
public long transferTo(final long position, final long count, final FileChannel target) throws IOException {
|
||||||
|
return target.transferFrom(new ConduitReadableByteChannel(this), position, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
public long transferTo(final long count, final ByteBuffer throughBuffer, final StreamSinkChannel target) throws IOException {
|
||||||
|
return IoUtils.transfer(new ConduitReadableByteChannel(this), count, throughBuffer, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read(ByteBuffer dst) throws IOException {
|
||||||
|
int pos = dst.position();
|
||||||
|
int res = super.read(dst);
|
||||||
|
if (res > 0) {
|
||||||
|
byte[] d = new byte[res];
|
||||||
|
for (int i = 0; i < res; ++i) {
|
||||||
|
d[i] = dst.get(i + pos);
|
||||||
|
}
|
||||||
|
data.add(d);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long read(ByteBuffer[] dsts, int offs, int len) throws IOException {
|
||||||
|
for (int i = offs; i < len; ++i) {
|
||||||
|
if (dsts[i].hasRemaining()) {
|
||||||
|
return read(dsts[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String dump() {
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (byte[] datum : data) {
|
||||||
|
sb.append(new String(datum, StandardCharsets.UTF_8));
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,23 @@
|
|||||||
|
package io.kamax.mxisd.http.undertow.conduit;
|
||||||
|
|
||||||
|
import io.undertow.server.ConduitWrapper;
|
||||||
|
import io.undertow.server.HttpServerExchange;
|
||||||
|
import io.undertow.util.ConduitFactory;
|
||||||
|
import org.xnio.conduits.Conduit;
|
||||||
|
|
||||||
|
public abstract class LazyConduitWrapper<T extends Conduit> implements ConduitWrapper<T> {
|
||||||
|
|
||||||
|
private T conduit = null;
|
||||||
|
|
||||||
|
protected abstract T create(ConduitFactory<T> factory, HttpServerExchange exchange);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T wrap(ConduitFactory<T> factory, HttpServerExchange exchange) {
|
||||||
|
conduit = create(factory, exchange);
|
||||||
|
return conduit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T get() {
|
||||||
|
return conduit;
|
||||||
|
}
|
||||||
|
}
|
@@ -33,6 +33,7 @@ import io.kamax.mxisd.util.RestClientUtils;
|
|||||||
import io.undertow.server.HttpHandler;
|
import io.undertow.server.HttpHandler;
|
||||||
import io.undertow.server.HttpServerExchange;
|
import io.undertow.server.HttpServerExchange;
|
||||||
import io.undertow.server.handlers.form.FormData;
|
import io.undertow.server.handlers.form.FormData;
|
||||||
|
import io.undertow.util.Headers;
|
||||||
import io.undertow.util.HttpString;
|
import io.undertow.util.HttpString;
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
@@ -189,7 +190,7 @@ public abstract class BasicHttpHandler implements HttpHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void respond(HttpServerExchange ex, int status, String errCode, String error) {
|
protected void respond(HttpServerExchange ex, int status, String errCode, String error) {
|
||||||
respond(ex, status, buildErrorBody(ex, errCode, error));
|
respond(ex, status, buildErrorBody(ex, errCode, error != null ? error : "An error has occurred"));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void handleException(HttpServerExchange exchange, HttpMatrixException ex) {
|
protected void handleException(HttpServerExchange exchange, HttpMatrixException ex) {
|
||||||
@@ -203,26 +204,34 @@ public abstract class BasicHttpHandler implements HttpHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void proxyPost(HttpServerExchange exchange, JsonObject body, CloseableHttpClient client, ClientDnsOverwrite dns) {
|
protected void proxyPost(HttpServerExchange exchange, JsonObject body, CloseableHttpClient client, ClientDnsOverwrite dns) {
|
||||||
|
proxyPost(exchange, body, client, dns, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void proxyPost(HttpServerExchange exchange, JsonObject body, CloseableHttpClient client, ClientDnsOverwrite dns,
|
||||||
|
boolean defaultJsonResponse) {
|
||||||
String target = dns.transform(URI.create(exchange.getRequestURL())).toString();
|
String target = dns.transform(URI.create(exchange.getRequestURL())).toString();
|
||||||
log.info("Requesting remote: {}", target);
|
log.info("Requesting remote: {}", target);
|
||||||
HttpPost req = RestClientUtils.post(target, GsonUtil.get(), body);
|
HttpPost req = RestClientUtils.post(target, GsonUtil.get(), body);
|
||||||
|
|
||||||
exchange.getRequestHeaders().forEach(header -> {
|
exchange.getRequestHeaders().forEach(header -> header.forEach(v -> {
|
||||||
header.forEach(v -> {
|
String name = header.getHeaderName().toString();
|
||||||
String name = header.getHeaderName().toString();
|
if (!StringUtils.startsWithIgnoreCase(name, "content-")) {
|
||||||
if (!StringUtils.startsWithIgnoreCase(name, "content-")) {
|
req.addHeader(name, v);
|
||||||
req.addHeader(name, v);
|
}
|
||||||
}
|
}));
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
|
boolean missingJsonResponse = true;
|
||||||
try (CloseableHttpResponse res = client.execute(req)) {
|
try (CloseableHttpResponse res = client.execute(req)) {
|
||||||
exchange.setStatusCode(res.getStatusLine().getStatusCode());
|
exchange.setStatusCode(res.getStatusLine().getStatusCode());
|
||||||
for (Header h : res.getAllHeaders()) {
|
for (Header h : res.getAllHeaders()) {
|
||||||
for (HeaderElement el : h.getElements()) {
|
for (HeaderElement el : h.getElements()) {
|
||||||
|
missingJsonResponse = !Headers.CONTENT_TYPE_STRING.equalsIgnoreCase(h.getName());
|
||||||
exchange.getResponseHeaders().add(HttpString.tryFromString(h.getName()), el.getValue());
|
exchange.getResponseHeaders().add(HttpString.tryFromString(h.getName()), el.getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (defaultJsonResponse && missingJsonResponse) {
|
||||||
|
exchange.getRequestHeaders().add(Headers.CONTENT_TYPE, "application/json");
|
||||||
|
}
|
||||||
res.getEntity().writeTo(exchange.getOutputStream());
|
res.getEntity().writeTo(exchange.getOutputStream());
|
||||||
exchange.endExchange();
|
exchange.endExchange();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
@@ -23,6 +23,7 @@ package io.kamax.mxisd.http.undertow.handler;
|
|||||||
import io.kamax.mxisd.auth.AccountManager;
|
import io.kamax.mxisd.auth.AccountManager;
|
||||||
import io.kamax.mxisd.config.PolicyConfig;
|
import io.kamax.mxisd.config.PolicyConfig;
|
||||||
import io.kamax.mxisd.exception.InvalidCredentialsException;
|
import io.kamax.mxisd.exception.InvalidCredentialsException;
|
||||||
|
import io.kamax.mxisd.exception.TermsNotSignedException;
|
||||||
import io.undertow.server.HttpHandler;
|
import io.undertow.server.HttpHandler;
|
||||||
import io.undertow.server.HttpServerExchange;
|
import io.undertow.server.HttpServerExchange;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@@ -66,7 +67,7 @@ public class CheckTermsHandler extends BasicHttpHandler {
|
|||||||
|
|
||||||
if (!accountManager.isTermAccepted(token, policies)) {
|
if (!accountManager.isTermAccepted(token, policies)) {
|
||||||
log.error("Non accepting request from: {}", exchange.getHostAndPort());
|
log.error("Non accepting request from: {}", exchange.getHostAndPort());
|
||||||
throw new InvalidCredentialsException();
|
throw new TermsNotSignedException();
|
||||||
}
|
}
|
||||||
log.trace("Access granted");
|
log.trace("Access granted");
|
||||||
child.handleRequest(exchange);
|
child.handleRequest(exchange);
|
||||||
|
@@ -0,0 +1,186 @@
|
|||||||
|
/*
|
||||||
|
* JBoss, Home of Professional Open Source.
|
||||||
|
* Copyright 2014 Red Hat, Inc., and individual contributors
|
||||||
|
* as indicated by the @author tags.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.kamax.mxisd.http.undertow.handler;
|
||||||
|
|
||||||
|
import io.kamax.mxisd.http.undertow.conduit.ConduitWithDump;
|
||||||
|
import io.kamax.mxisd.http.undertow.conduit.DebuggingStreamSinkConduit;
|
||||||
|
import io.kamax.mxisd.http.undertow.conduit.DebuggingStreamSourceConduit;
|
||||||
|
import io.kamax.mxisd.http.undertow.conduit.LazyConduitWrapper;
|
||||||
|
import io.undertow.security.api.SecurityContext;
|
||||||
|
import io.undertow.server.HttpHandler;
|
||||||
|
import io.undertow.server.HttpServerExchange;
|
||||||
|
import io.undertow.server.handlers.Cookie;
|
||||||
|
import io.undertow.util.ConduitFactory;
|
||||||
|
import io.undertow.util.HeaderValues;
|
||||||
|
import io.undertow.util.Headers;
|
||||||
|
import io.undertow.util.LocaleUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.xnio.conduits.StreamSinkConduit;
|
||||||
|
import org.xnio.conduits.StreamSourceConduit;
|
||||||
|
|
||||||
|
import java.util.Deque;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler that dumps a exchange to a log.
|
||||||
|
*
|
||||||
|
* @author Stuart Douglas
|
||||||
|
*/
|
||||||
|
public class RequestDumpingHandler implements HttpHandler {
|
||||||
|
|
||||||
|
private static final Logger LOGGER = LoggerFactory.getLogger(RequestDumpingHandler.class);
|
||||||
|
|
||||||
|
private final HttpHandler next;
|
||||||
|
|
||||||
|
public RequestDumpingHandler(HttpHandler next) {
|
||||||
|
this.next = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleRequest(HttpServerExchange exchange) throws Exception {
|
||||||
|
LazyConduitWrapper<StreamSourceConduit> requestConduitWrapper = new LazyConduitWrapper<StreamSourceConduit>() {
|
||||||
|
@Override
|
||||||
|
protected StreamSourceConduit create(ConduitFactory<StreamSourceConduit> factory, HttpServerExchange exchange) {
|
||||||
|
return new DebuggingStreamSourceConduit(factory.create());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
LazyConduitWrapper<StreamSinkConduit> responseConduitWrapper = new LazyConduitWrapper<StreamSinkConduit>() {
|
||||||
|
@Override
|
||||||
|
protected StreamSinkConduit create(ConduitFactory<StreamSinkConduit> factory, HttpServerExchange exchange) {
|
||||||
|
return new DebuggingStreamSinkConduit(factory.create());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
exchange.addRequestWrapper(requestConduitWrapper);
|
||||||
|
exchange.addResponseWrapper(responseConduitWrapper);
|
||||||
|
|
||||||
|
final StringBuilder sb = new StringBuilder();
|
||||||
|
// Log pre-service information
|
||||||
|
final SecurityContext sc = exchange.getSecurityContext();
|
||||||
|
sb.append("\n----------------------------REQUEST---------------------------\n");
|
||||||
|
sb.append(" URI=").append(exchange.getRequestURI()).append("\n");
|
||||||
|
sb.append(" characterEncoding=").append(exchange.getRequestHeaders().get(Headers.CONTENT_ENCODING)).append("\n");
|
||||||
|
sb.append(" contentLength=").append(exchange.getRequestContentLength()).append("\n");
|
||||||
|
sb.append(" contentType=").append(exchange.getRequestHeaders().get(Headers.CONTENT_TYPE)).append("\n");
|
||||||
|
//sb.append(" contextPath=" + exchange.getContextPath());
|
||||||
|
if (sc != null) {
|
||||||
|
if (sc.isAuthenticated()) {
|
||||||
|
sb.append(" authType=").append(sc.getMechanismName()).append("\n");
|
||||||
|
sb.append(" principle=").append(sc.getAuthenticatedAccount().getPrincipal()).append("\n");
|
||||||
|
} else {
|
||||||
|
sb.append(" authType=none\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Cookie> cookies = exchange.getRequestCookies();
|
||||||
|
if (cookies != null) {
|
||||||
|
for (Map.Entry<String, Cookie> entry : cookies.entrySet()) {
|
||||||
|
Cookie cookie = entry.getValue();
|
||||||
|
sb.append(" cookie=").append(cookie.getName()).append("=").append(cookie.getValue()).append("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (HeaderValues header : exchange.getRequestHeaders()) {
|
||||||
|
for (String value : header) {
|
||||||
|
sb.append(" header=").append(header.getHeaderName()).append("=").append(value).append("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sb.append(" locale=").append(LocaleUtils.getLocalesFromHeader(exchange.getRequestHeaders().get(Headers.ACCEPT_LANGUAGE)))
|
||||||
|
.append("\n");
|
||||||
|
sb.append(" method=").append(exchange.getRequestMethod()).append("\n");
|
||||||
|
Map<String, Deque<String>> pnames = exchange.getQueryParameters();
|
||||||
|
for (Map.Entry<String, Deque<String>> entry : pnames.entrySet()) {
|
||||||
|
String pname = entry.getKey();
|
||||||
|
Iterator<String> pvalues = entry.getValue().iterator();
|
||||||
|
sb.append(" parameter=");
|
||||||
|
sb.append(pname);
|
||||||
|
sb.append('=');
|
||||||
|
while (pvalues.hasNext()) {
|
||||||
|
sb.append(pvalues.next());
|
||||||
|
if (pvalues.hasNext()) {
|
||||||
|
sb.append(", ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sb.append("\n");
|
||||||
|
}
|
||||||
|
//sb.append(" pathInfo=" + exchange.getPathInfo());
|
||||||
|
sb.append(" protocol=").append(exchange.getProtocol()).append("\n");
|
||||||
|
sb.append(" queryString=").append(exchange.getQueryString()).append("\n");
|
||||||
|
sb.append(" remoteAddr=").append(exchange.getSourceAddress()).append("\n");
|
||||||
|
sb.append(" remoteHost=").append(exchange.getSourceAddress().getHostName()).append("\n");
|
||||||
|
//sb.append("requestedSessionId=" + exchange.getRequestedSessionId());
|
||||||
|
sb.append(" scheme=").append(exchange.getRequestScheme()).append("\n");
|
||||||
|
sb.append(" host=").append(exchange.getRequestHeaders().getFirst(Headers.HOST)).append("\n");
|
||||||
|
sb.append(" serverPort=").append(exchange.getDestinationAddress().getPort()).append("\n");
|
||||||
|
//sb.append(" servletPath=" + exchange.getServletPath());
|
||||||
|
sb.append(" isSecure=").append(exchange.isSecure()).append("\n");
|
||||||
|
|
||||||
|
exchange.addExchangeCompleteListener((exchange1, nextListener) -> {
|
||||||
|
StreamSourceConduit sourceConduit = requestConduitWrapper.get();
|
||||||
|
if (sourceConduit instanceof ConduitWithDump) {
|
||||||
|
ConduitWithDump conduitWithDump = (ConduitWithDump) sourceConduit;
|
||||||
|
sb.append("body=\n");
|
||||||
|
sb.append(conduitWithDump.dump()).append("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log post-service information
|
||||||
|
sb.append("--------------------------RESPONSE--------------------------\n");
|
||||||
|
if (sc != null) {
|
||||||
|
if (sc.isAuthenticated()) {
|
||||||
|
sb.append(" authType=").append(sc.getMechanismName()).append("\n");
|
||||||
|
sb.append(" principle=").append(sc.getAuthenticatedAccount().getPrincipal()).append("\n");
|
||||||
|
} else {
|
||||||
|
sb.append(" authType=none\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sb.append(" contentLength=").append(exchange1.getResponseContentLength()).append("\n");
|
||||||
|
sb.append(" contentType=").append(exchange1.getResponseHeaders().getFirst(Headers.CONTENT_TYPE)).append("\n");
|
||||||
|
Map<String, Cookie> cookies1 = exchange1.getResponseCookies();
|
||||||
|
if (cookies1 != null) {
|
||||||
|
for (Cookie cookie : cookies1.values()) {
|
||||||
|
sb.append(" cookie=").append(cookie.getName()).append("=").append(cookie.getValue()).append("; domain=")
|
||||||
|
.append(cookie.getDomain()).append("; path=").append(cookie.getPath()).append("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (HeaderValues header : exchange1.getResponseHeaders()) {
|
||||||
|
for (String value : header) {
|
||||||
|
sb.append(" header=").append(header.getHeaderName()).append("=").append(value).append("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sb.append(" status=").append(exchange1.getStatusCode()).append("\n");
|
||||||
|
StreamSinkConduit streamSinkConduit = responseConduitWrapper.get();
|
||||||
|
if (streamSinkConduit instanceof ConduitWithDump) {
|
||||||
|
ConduitWithDump conduitWithDump = (ConduitWithDump) streamSinkConduit;
|
||||||
|
sb.append("body=\n");
|
||||||
|
sb.append(conduitWithDump.dump());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.append("\n==============================================================");
|
||||||
|
|
||||||
|
|
||||||
|
nextListener.proceed();
|
||||||
|
LOGGER.info(sb.toString());
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// Perform the exchange
|
||||||
|
next.handleRequest(exchange);
|
||||||
|
}
|
||||||
|
}
|
@@ -82,7 +82,10 @@ public class SaneHandler extends BasicHttpHandler {
|
|||||||
} catch (InvalidJsonException e) {
|
} catch (InvalidJsonException e) {
|
||||||
respond(exchange, HttpStatus.SC_BAD_REQUEST, e.getErrorCode(), e.getError());
|
respond(exchange, HttpStatus.SC_BAD_REQUEST, e.getErrorCode(), e.getError());
|
||||||
} catch (InvalidCredentialsException e) {
|
} catch (InvalidCredentialsException e) {
|
||||||
|
log.error("Unauthorized: ", e);
|
||||||
respond(exchange, HttpStatus.SC_UNAUTHORIZED, "M_UNAUTHORIZED", e.getMessage());
|
respond(exchange, HttpStatus.SC_UNAUTHORIZED, "M_UNAUTHORIZED", e.getMessage());
|
||||||
|
} catch (TermsNotSignedException e) {
|
||||||
|
respond(exchange, HttpStatus.SC_FORBIDDEN, "M_TERMS_NOT_SIGNED", e.getMessage());
|
||||||
} catch (ObjectNotFoundException e) {
|
} catch (ObjectNotFoundException e) {
|
||||||
respond(exchange, HttpStatus.SC_NOT_FOUND, "M_NOT_FOUND", e.getMessage());
|
respond(exchange, HttpStatus.SC_NOT_FOUND, "M_NOT_FOUND", e.getMessage());
|
||||||
} catch (NotImplementedException e) {
|
} catch (NotImplementedException e) {
|
||||||
|
@@ -29,10 +29,7 @@ import java.util.Optional;
|
|||||||
public abstract class ApplicationServiceHandler extends BasicHttpHandler {
|
public abstract class ApplicationServiceHandler extends BasicHttpHandler {
|
||||||
|
|
||||||
protected String getToken(HttpServerExchange ex) {
|
protected String getToken(HttpServerExchange ex) {
|
||||||
return Optional.ofNullable(ex.getQueryParameters()
|
return getAccessToken(ex);
|
||||||
.getOrDefault("access_token", new LinkedList<>())
|
|
||||||
.peekFirst()
|
|
||||||
).orElse("");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -24,6 +24,6 @@ import io.kamax.mxisd.http.undertow.handler.BasicHttpHandler;
|
|||||||
|
|
||||||
public abstract class LoginHandler extends BasicHttpHandler {
|
public abstract class LoginHandler extends BasicHttpHandler {
|
||||||
|
|
||||||
public static final String Path = "/_matrix/client/r0/login";
|
public static final String Path = "/_matrix/client/v3/login";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -48,7 +48,7 @@ public class AccountRegisterHandler extends BasicHttpHandler {
|
|||||||
|
|
||||||
if (LOGGER.isInfoEnabled()) {
|
if (LOGGER.isInfoEnabled()) {
|
||||||
LOGGER.info("Registration from domain: {}, expired at {}", openIdToken.getMatrixServerName(),
|
LOGGER.info("Registration from domain: {}, expired at {}", openIdToken.getMatrixServerName(),
|
||||||
new Date(openIdToken.getExpiresIn()));
|
new Date(System.currentTimeMillis() + openIdToken.getExpiresIn()));
|
||||||
}
|
}
|
||||||
|
|
||||||
String token = accountManager.register(openIdToken);
|
String token = accountManager.register(openIdToken);
|
||||||
|
@@ -31,7 +31,7 @@ import java.net.URI;
|
|||||||
|
|
||||||
public class UserDirectorySearchHandler extends HomeserverProxyHandler {
|
public class UserDirectorySearchHandler extends HomeserverProxyHandler {
|
||||||
|
|
||||||
public static final String Path = "/_matrix/client/r0/user_directory/search";
|
public static final String Path = "/_matrix/client/v3/user_directory/search";
|
||||||
|
|
||||||
private DirectoryManager mgr;
|
private DirectoryManager mgr;
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user