Compare commits
224 Commits
Author | SHA1 | Date | |
---|---|---|---|
0de8e247cb | |||
8d4f331880 | |||
7f1b77ce19 | |||
d4080bb2e5 | |||
0106364680 | |||
b1ef1b9aa1 | |||
84b4c9c3ed | |||
dedce04961 | |||
a4e99fcc79 | |||
d568678df5 | |||
31210f4e71 | |||
df70b65ac3 | |||
949bfc38f8 | |||
c5e5f643a2 | |||
5820156512 | |||
7cc4be7cd6 | |||
5e31ff9b69 | |||
d910472db5 | |||
ca56afad5d | |||
541aeee3c2 | |||
19257a0290 | |||
8d91e7d5a3 | |||
c0671acf60 | |||
0b8300c774 | |||
cac55d5f06 | |||
aa3aadbb53 | |||
da7fa9edd6 | |||
a1a21e8d60 | |||
dc2932cf51 | |||
bb84e32719 | |||
12aa40b160 | |||
9d4fddcb2d | |||
4654cff158 | |||
b1888fed96 | |||
d7e8b3d62a | |||
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 | ||
|
17ebc2a421 | ||
|
cbb9fced8d | ||
|
7509174611 | ||
|
51d9225dda | ||
|
6216113400 | ||
|
cb32441959 | ||
|
0ec4df2c06 | ||
|
86b880069b | ||
|
a97273fe77 | ||
|
f9daf4d58a | ||
|
9e4cabb69b | ||
|
0b81de3cd0 | ||
|
698a16ec17 | ||
|
619b70d860 | ||
|
494c9e3941 | ||
|
94441d0446 | ||
|
b4776b50e2 | ||
|
2458b38b75 | ||
|
249e28a8b5 | ||
|
ba9e2d6121 | ||
|
f042b82a50 | ||
|
59071177ad | ||
|
6450cd1f20 | ||
|
90bc244f3e | ||
|
6e52a509db | ||
|
5ca666981a |
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/
|
||||
|
||||
# Local dev config
|
||||
/ma1sd.yaml
|
||||
/mxids.yaml
|
||||
/application.yaml
|
||||
|
||||
# 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:21-jdk-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
|
||||
VOLUME /var/ma1sd
|
||||
WORKDIR /mxids
|
||||
COPY . .
|
||||
RUN ./gradlew shadowJar
|
||||
|
||||
# Second stage: Setup the runtime container
|
||||
FROM openjdk:21-jdk-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
|
||||
|
||||
ENV JAVA_OPTS=""
|
||||
ENV CONF_FILE_PATH="/etc/ma1sd/ma1sd.yaml"
|
||||
ENV SIGN_KEY_PATH="/var/ma1sd/sign.key"
|
||||
ENV SQLITE_DATABASE_PATH="/var/ma1sd/ma1sd.db"
|
||||
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" ]
|
||||
# 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
|
||||
ADD src/script/ma1sd /app/ma1sd
|
||||
ADD build/libs/ma1sd.jar /app/ma1sd.jar
|
||||
CMD ["/start.sh"]
|
||||
|
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
|
||||
*
|
||||
* https://www.kamax.io/
|
||||
@@ -20,22 +20,22 @@
|
||||
|
||||
import java.util.regex.Pattern
|
||||
|
||||
apply plugin: 'java'
|
||||
apply plugin: 'java-library'
|
||||
apply plugin: 'application'
|
||||
apply plugin: 'com.github.johnrengelman.shadow'
|
||||
apply plugin: 'idea'
|
||||
apply plugin: 'com.github.ben-manes.versions'
|
||||
|
||||
def confFileName = "ma1sd.example.yaml"
|
||||
def confFileName = "mxids.example.yaml"
|
||||
def distDir = "${project.buildDir}/dist"
|
||||
|
||||
def debBinPath = "/usr/lib/ma1sd"
|
||||
def debConfPath = "/etc/ma1sd"
|
||||
def debDataPath = "/var/lib/ma1sd"
|
||||
def debBinPath = "/usr/lib/mxids"
|
||||
def debConfPath = "/etc/mxids"
|
||||
def debDataPath = "/var/lib/mxids"
|
||||
def debSystemdPath = "/etc/systemd/system"
|
||||
|
||||
def debConfFileName = confFileName
|
||||
def debStartScriptFilename = "ma1sd"
|
||||
def debStartScriptFilename = "mxids"
|
||||
|
||||
def debBuildBasePath = "${project.buildDir}/tmp/debian"
|
||||
def debBuildDebianPath = "${debBuildBasePath}/DEBIAN"
|
||||
@@ -44,18 +44,18 @@ def debBuildConfPath = "${debBuildBasePath}${debConfPath}"
|
||||
def debBuildDataPath = "${debBuildBasePath}${debDataPath}"
|
||||
def debBuildSystemdPath = "${debBuildBasePath}${debSystemdPath}"
|
||||
|
||||
def dockerImageName = "ma1uta/ma1sd"
|
||||
def dockerImageTag = "${dockerImageName}:${ma1sdVersion()}"
|
||||
def dockerImageName = "cqrenet/mxids"
|
||||
def dockerImageTag = "${dockerImageName}:${mxidsVersion()}"
|
||||
|
||||
group = 'io.kamax'
|
||||
mainClassName = 'io.kamax.mxisd.MxisdStandaloneExec'
|
||||
sourceCompatibility = '1.8'
|
||||
targetCompatibility = '1.8'
|
||||
sourceCompatibility = '11'
|
||||
targetCompatibility = '11'
|
||||
|
||||
String ma1sdVersion() {
|
||||
String mxidsVersion() {
|
||||
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) {
|
||||
version = gitVersion()
|
||||
}
|
||||
@@ -73,109 +73,114 @@ String gitVersion() {
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
jcenter()
|
||||
gradlePluginPortal()
|
||||
mavenCentral()
|
||||
google()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.github.jengelman.gradle.plugins:shadow:5.1.0'
|
||||
classpath 'com.github.ben-manes:gradle-versions-plugin:0.27.0'
|
||||
classpath 'com.github.johnrengelman:shadow:8.1.1'
|
||||
classpath 'com.github.ben-manes:gradle-versions-plugin:0.52.0'
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// Logging
|
||||
compile 'org.slf4j:slf4j-simple:1.7.25'
|
||||
api 'org.slf4j:slf4j-simple:2.0.13'
|
||||
|
||||
// Easy file management
|
||||
compile 'commons-io:commons-io:2.6'
|
||||
api 'commons-io:commons-io:2.20.0'
|
||||
|
||||
// Config management
|
||||
compile 'org.yaml:snakeyaml:1.25'
|
||||
api 'org.yaml:snakeyaml:1.33'
|
||||
|
||||
// Dependencies from old Matrix-java-sdk
|
||||
compile 'org.apache.commons:commons-lang3:3.9'
|
||||
compile 'com.squareup.okhttp3:okhttp:4.2.2'
|
||||
compile 'commons-codec:commons-codec:1.13'
|
||||
api 'org.apache.commons:commons-lang3:3.18.0'
|
||||
api 'com.squareup.okhttp3:okhttp:4.12.0'
|
||||
api 'commons-codec:commons-codec:1.19.0'
|
||||
|
||||
// ORMLite
|
||||
compile 'com.j256.ormlite:ormlite-jdbc:5.1'
|
||||
api 'com.j256.ormlite:ormlite-jdbc:6.1'
|
||||
|
||||
// ed25519 handling
|
||||
compile 'net.i2p.crypto:eddsa:0.3.0'
|
||||
api 'net.i2p.crypto:eddsa:0.3.0'
|
||||
|
||||
// LDAP connector
|
||||
compile 'org.apache.directory.api:api-all:1.0.0'
|
||||
api 'org.apache.directory.api:api-all:2.1.7'
|
||||
|
||||
// DNS lookups
|
||||
compile 'dnsjava:dnsjava:2.1.9'
|
||||
api 'dnsjava:dnsjava:3.6.3'
|
||||
|
||||
// HTTP connections
|
||||
compile 'org.apache.httpcomponents:httpclient:4.5.10'
|
||||
api 'org.apache.httpcomponents:httpclient:4.5.14'
|
||||
|
||||
// Phone numbers validation
|
||||
compile 'com.googlecode.libphonenumber:libphonenumber:8.10.22'
|
||||
api 'com.googlecode.libphonenumber:libphonenumber:8.13.36'
|
||||
|
||||
// E-mail sending
|
||||
compile 'javax.mail:javax.mail-api:1.6.2'
|
||||
compile 'com.sun.mail:javax.mail:1.6.2'
|
||||
api 'javax.mail:javax.mail-api:1.6.2'
|
||||
api 'com.sun.mail:javax.mail:1.6.2'
|
||||
|
||||
// Google Firebase Authentication backend
|
||||
compile 'com.google.firebase:firebase-admin:5.3.0'
|
||||
api 'com.google.firebase:firebase-admin:9.2.0'
|
||||
|
||||
// Connection Pool
|
||||
compile 'com.mchange:c3p0:0.9.5.4'
|
||||
api 'com.mchange:c3p0:0.11.2'
|
||||
|
||||
// SQLite
|
||||
compile 'org.xerial:sqlite-jdbc:3.28.0'
|
||||
api 'org.xerial:sqlite-jdbc:3.50.3.0'
|
||||
|
||||
// PostgreSQL
|
||||
compile 'org.postgresql:postgresql:42.2.8'
|
||||
api 'org.postgresql:postgresql:42.7.7'
|
||||
|
||||
// MariaDB/MySQL
|
||||
compile 'org.mariadb.jdbc:mariadb-java-client:2.5.1'
|
||||
api 'org.mariadb.jdbc:mariadb-java-client:3.5.6'
|
||||
|
||||
// UNIX sockets
|
||||
api 'com.kohlschutter.junixsocket:junixsocket-core:2.9.1'
|
||||
|
||||
// Twilio SDK for SMS
|
||||
compile 'com.twilio.sdk:twilio:7.45.0'
|
||||
api 'com.twilio.sdk:twilio:10.9.2'
|
||||
|
||||
// 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
|
||||
compile 'org.zeroturnaround:zt-exec:1.11'
|
||||
api 'org.zeroturnaround:zt-exec:1.12'
|
||||
|
||||
// HTTP server
|
||||
compile 'io.undertow:undertow-core:2.0.27.Final'
|
||||
api 'io.undertow:undertow-core:2.3.13.Final'
|
||||
|
||||
// Command parser for AS interface
|
||||
implementation 'commons-cli:commons-cli:1.4'
|
||||
api 'commons-cli:commons-cli:1.7.0'
|
||||
|
||||
testCompile 'junit:junit:4.13-rc-1'
|
||||
testCompile 'com.github.tomakehurst:wiremock:2.25.1'
|
||||
testCompile 'com.unboundid:unboundid-ldapsdk:4.0.12'
|
||||
testCompile 'com.icegreen:greenmail:1.5.11'
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
testImplementation 'com.github.tomakehurst:wiremock:3.0.1'
|
||||
testImplementation 'com.unboundid:unboundid-ldapsdk:7.0.0'
|
||||
testImplementation 'com.icegreen:greenmail:1.6.15'
|
||||
}
|
||||
|
||||
jar {
|
||||
manifest {
|
||||
attributes(
|
||||
'Implementation-Version': ma1sdVersion()
|
||||
'Implementation-Version': mxidsVersion()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
shadowJar {
|
||||
baseName = project.name
|
||||
classifier = null
|
||||
version = null
|
||||
archiveBaseName.set(project.name)
|
||||
archiveClassifier.set('') // Set to an empty string if you don't need a classifier.
|
||||
archiveVersion.set('') // Set to an empty string if you don't want the version in the jar name.
|
||||
}
|
||||
|
||||
task debBuild(dependsOn: shadowJar) {
|
||||
doLast {
|
||||
String debVersion = ma1sdVersion()
|
||||
String debVersion = mxidsVersion()
|
||||
println "Version for package: ${debVersion}"
|
||||
mkdir distDir
|
||||
mkdir debBuildBasePath
|
||||
@@ -186,7 +191,7 @@ task debBuild(dependsOn: shadowJar) {
|
||||
mkdir debBuildSystemdPath
|
||||
|
||||
copy {
|
||||
from "${project.buildDir}/libs/ma1sd.jar"
|
||||
from "${project.buildDir}/libs/mxids.jar"
|
||||
into debBuildBinPath
|
||||
}
|
||||
|
||||
@@ -234,12 +239,12 @@ task debBuild(dependsOn: shadowJar) {
|
||||
ant.replace(
|
||||
file: "${debBuildDebianPath}/postinst",
|
||||
token: '%DEB_CONF_FILE%',
|
||||
value: "${debConfPath}/ma1sd.yaml"
|
||||
value: "${debConfPath}/mxids.yaml"
|
||||
)
|
||||
|
||||
ant.chmod(
|
||||
file: "${debBuildDebianPath}/postinst",
|
||||
perm: 'a+x'
|
||||
perm: '0755'
|
||||
)
|
||||
|
||||
ant.chmod(
|
||||
@@ -248,7 +253,7 @@ task debBuild(dependsOn: shadowJar) {
|
||||
)
|
||||
|
||||
copy {
|
||||
from "${project.file('src/systemd/ma1sd.service')}"
|
||||
from "${project.file('src/systemd/mxids.service')}"
|
||||
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
|
||||
|
||||
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) {
|
||||
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
|
||||
}
|
||||
|
146
docs/MSC2140_MSC2134.md
Normal file
146
docs/MSC2140_MSC2134.md
Normal file
@@ -0,0 +1,146 @@
|
||||
# MSC2140
|
||||
|
||||
## V1 vs V2
|
||||
In the [MSC2140](https://github.com/matrix-org/matrix-doc/pull/2140) the v2 prefix was introduced.
|
||||
|
||||
Default values:
|
||||
```.yaml
|
||||
matrix:
|
||||
v1: true # deprecated
|
||||
v2: false
|
||||
```
|
||||
|
||||
To disable change value to `false`.
|
||||
|
||||
NOTE: the v1 is deprecated, therefore recommend to use only v2 and disable v1 (default value can be ommited):
|
||||
```.yaml
|
||||
matrix:
|
||||
v1: false
|
||||
```
|
||||
NOTE: Riot Web version 1.5.5 and below checks the v1 for backward compatibility.
|
||||
|
||||
NOTE: v2 disabled by default in order to preserve backward compatibility.
|
||||
|
||||
## Terms
|
||||
|
||||
###### Requires: No.
|
||||
|
||||
Administrator can omit terms configuration. In this case the terms checking will be disabled.
|
||||
|
||||
Example:
|
||||
```.yaml
|
||||
policy:
|
||||
policies:
|
||||
term_name: # term name
|
||||
version: 1.0 # version
|
||||
terms:
|
||||
en: # lang
|
||||
name: term name en # localized name
|
||||
url: https://ma1sd.host.tld/term_en.html # localized url
|
||||
fe: # lang
|
||||
name: term name fr # localized name
|
||||
url: https://ma1sd.host.tld/term_fr.html # localized url
|
||||
regexp:
|
||||
- '/_matrix/identity/v2/account.*'
|
||||
- '/_matrix/identity/v2/hash_details'
|
||||
- '/_matrix/identity/v2/lookup'
|
||||
```
|
||||
Where:
|
||||
|
||||
- `term_name` -- name of the terms.
|
||||
- `version` -- the terms version.
|
||||
- `lang` -- the term language.
|
||||
- `name` -- the name of the term.
|
||||
- `url` -- the url of the term. 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.
|
||||
|
||||
API will be checks for accepted terms only with authorization.
|
||||
There are the next API:
|
||||
- [`GET /_matrix/identity/v2/account`](https://matrix.org/docs/spec/identity_service/r0.3.0#get-matrix-identity-v2-account) - Gets information about what user owns the access token used in the request.
|
||||
- [`POST /_matrix/identity/v2/account/logout`](https://matrix.org/docs/spec/identity_service/r0.3.0#post-matrix-identity-v2-account-logout) - Logs out the access token, preventing it from being used to authenticate future requests to the server.
|
||||
- [`GET /_matrix/identity/v2/hash_details`](https://matrix.org/docs/spec/identity_service/r0.3.0#get-matrix-identity-v2-hash-details) - Gets parameters for hashing identifiers from the server. This can include any of the algorithms defined in this specification.
|
||||
- [`POST /_matrix/identity/v2/lookup`](https://matrix.org/docs/spec/identity_service/r0.3.0#post-matrix-identity-v2-lookup) - Looks up the set of Matrix User IDs which have bound the 3PIDs given, if bindings are available. Note that the format of the addresses is defined later in this specification.
|
||||
- [`POST /_matrix/identity/v2/validate/email/requestToken`](https://matrix.org/docs/spec/identity_service/r0.3.0#post-matrix-identity-v2-validate-email-requesttoken) - Create a session for validating an email address.
|
||||
- [`POST /_matrix/identity/v2/validate/email/submitToken`](https://matrix.org/docs/spec/identity_service/r0.3.0#post-matrix-identity-v2-validate-email-submittoken) - Validate ownership of an email address.
|
||||
- [`GET /_matrix/identity/v2/validate/email/submitToken`](https://matrix.org/docs/spec/identity_service/r0.3.0#get-matrix-identity-v2-validate-email-submittoken) - Validate ownership of an email address.
|
||||
- [`POST /_matrix/identity/v2/validate/msisdn/requestToken`](https://matrix.org/docs/spec/identity_service/r0.3.0#post-matrix-identity-v2-validate-msisdn-requesttoken) - Create a session for validating a phone number.
|
||||
- [`POST /_matrix/identity/v2/validate/msisdn/submitToken`](https://matrix.org/docs/spec/identity_service/r0.3.0#post-matrix-identity-v2-validate-msisdn-submittoken) - Validate ownership of a phone number.
|
||||
- [`GET /_matrix/identity/v2/validate/msisdn/submitToken`](https://matrix.org/docs/spec/identity_service/r0.3.0#get-matrix-identity-v2-validate-msisdn-submittoken) - Validate ownership of a phone number.
|
||||
- [`GET /_matrix/identity/v2/3pid/getValidated3pid`](https://matrix.org/docs/spec/identity_service/r0.3.0#get-matrix-identity-v2-3pid-getvalidated3pid) - Determines if a given 3pid has been validated by a user.
|
||||
- [`POST /_matrix/identity/v2/3pid/bind`](https://matrix.org/docs/spec/identity_service/r0.3.0#post-matrix-identity-v2-3pid-bind) - Publish an association between a session and a Matrix user ID.
|
||||
- [`POST /_matrix/identity/v2/3pid/unbind`](https://matrix.org/docs/spec/identity_service/r0.3.0#post-matrix-identity-v2-3pid-unbind) - Remove an association between a session and a Matrix user ID.
|
||||
- [`POST /_matrix/identity/v2/store-invite`](https://matrix.org/docs/spec/identity_service/r0.3.0#post-matrix-identity-v2-store-invite) - Store pending invitations to a user's 3pid.
|
||||
- [`POST /_matrix/identity/v2/sign-ed25519`](https://matrix.org/docs/spec/identity_service/r0.3.0#post-matrix-identity-v2-sign-ed25519) - Sign invitation details.
|
||||
|
||||
There is only one exception: [`POST /_matrix/identity/v2/terms`](https://matrix.org/docs/spec/identity_service/r0.3.0#post-matrix-identity-v2-terms) which uses for accepting the terms and requires the authorization.
|
||||
|
||||
## [Hash lookup](https://github.com/matrix-org/matrix-doc/blob/hs/hash-identity/proposals/2134-identity-hash-lookup.md)
|
||||
|
||||
Hashes and the pepper updates together according to the `rotationPolicy`.
|
||||
|
||||
###### Requires: No.
|
||||
|
||||
In case the `none` algorithms ma1sd will be lookup using the v1 bulk API.
|
||||
|
||||
```.yaml
|
||||
hashing:
|
||||
enabled: true # enable or disable the hash lookup MSC2140 (default is false)
|
||||
pepperLength: 20 # length of the pepper value (default is 20)
|
||||
rotationPolicy: per_requests # or `per_seconds` how often the hashes will be updating
|
||||
hashStorageType: sql # or `in_memory` where the hashes will be stored
|
||||
algorithms:
|
||||
- none # the same as v1 bulk lookup
|
||||
- sha256 # hash the 3PID and pepper.
|
||||
delay: 2m # how often hashes will be updated if rotation policy = per_seconds (default is 10s)
|
||||
requests: 10 # how many lookup requests will be performed before updating hashes if rotation policy = per_requests (default is 10)
|
||||
```
|
||||
|
||||
When enabled and client requests the `none` algorithms then hash lookups works as v1 bulk lookup.
|
||||
|
||||
Delay specified in the format: `2d 4h 12m 34s` - this means 2 days 4 hours 12 minutes and 34 seconds. Zero units may be omitted. For example:
|
||||
|
||||
- 12s - 12 seconds
|
||||
- 3m - 3 minutes
|
||||
- 5m 6s - 5 minutes and 6 seconds
|
||||
- 6h 3s - 6 hours and 3 seconds
|
||||
|
||||
|
||||
Sha256 algorithm supports only sql, memory and exec 3PID providers.
|
||||
For sql provider (i.e. for the `synapseSql`):
|
||||
```.yaml
|
||||
synapseSql:
|
||||
lookup:
|
||||
query: 'select user_id as mxid, medium, address from user_threepid_id_server' # query for retrive 3PIDs for hashes.
|
||||
```
|
||||
|
||||
For general sql provider:
|
||||
```.yaml
|
||||
sql:
|
||||
lookup:
|
||||
query: 'select user as mxid, field1 as medium, field2 as address from some_table' # query for retrive 3PIDs for hashes.
|
||||
```
|
||||
|
||||
Each query should return the `mxid`, `medium` and `address` fields.
|
||||
|
||||
|
||||
For memory providers:
|
||||
```.yaml
|
||||
memory:
|
||||
hashEnabled: true # enable the hash lookup (defaults is false)
|
||||
```
|
||||
|
||||
For exec providers:
|
||||
```.yaml
|
||||
exec:
|
||||
identity:
|
||||
hashEnabled: true # enable the hash lookup (defaults is false)
|
||||
```
|
||||
|
||||
For ldap providers:
|
||||
```.yaml
|
||||
ldap:
|
||||
lookup: true
|
||||
```
|
||||
|
||||
NOTE: Federation requests work only with `none` algorithms.
|
||||
|
@@ -16,7 +16,7 @@ TCP 443
|
||||
+<---------------------------------<+
|
||||
|
|
||||
| +-------------------+
|
||||
TCP 8090 +-> | ma1sd |
|
||||
TCP 8090 +-> | mxids |
|
||||
| |
|
||||
| - Profile's 3PIDs |
|
||||
| - 3PID Invites |
|
||||
|
@@ -1,14 +1,18 @@
|
||||
# From source
|
||||
- [Binaries](#binaries)
|
||||
- [Requirements](#requirements)
|
||||
- [Build](#build)
|
||||
- [Debian package](#debian-package)
|
||||
- [Docker image](#docker-image)
|
||||
- [Next steps](#next-steps)
|
||||
- [From source](#from-source)
|
||||
- [Binaries](#binaries)
|
||||
- [Requirements](#requirements)
|
||||
- [Build](#build)
|
||||
- [Debian package](#debian-package)
|
||||
- [Docker image](#docker-image)
|
||||
- [Multi-platform builds](#multi-platform-builds)
|
||||
- [Next steps](#next-steps)
|
||||
|
||||
## Binaries
|
||||
### Requirements
|
||||
- JDK 1.8
|
||||
- OpenJDK 11
|
||||
- OpenJDK 14
|
||||
|
||||
### Build
|
||||
```bash
|
||||
@@ -17,7 +21,7 @@ cd ma1sd
|
||||
./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).
|
||||
|
||||
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.
|
||||
|
||||
### 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
|
||||
- [Integrate with your infrastructure](getting-started.md#integrate)
|
||||
|
@@ -48,6 +48,9 @@ Create a list under the label `myOtherServers` containing two Identity servers:
|
||||
## Unbind (MSC1915)
|
||||
- `session.policy.unbind.enabled`: Enable or disable unbind functionality (MSC1915). (Defaults to true).
|
||||
|
||||
## Hash lookups, Term and others (MSC2140, MSC2134)
|
||||
See the [dedicated document](MSC2140_MSC2134.md) for configuration.
|
||||
|
||||
*Warning*: Unbind check incoming request by two ways:
|
||||
- session validation.
|
||||
- request signature via `X-Matrix` header and uses `server.publicUrl` property to construct the signing json;
|
||||
@@ -55,7 +58,53 @@ Commonly the `server.publicUrl` should be the same value as the `trusted_third_p
|
||||
|
||||
## Storage
|
||||
### 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
|
||||
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.
|
||||
|
||||
### I already use the synapse LDAP3 auth provider. Why should I care about ma1sd?
|
||||
The [synapse LDAP3 auth provider](https://github.com/matrix-org/matrix-synapse-ldap3) is not longer maintained despite
|
||||
saying so and only handles on specific flow: validate credentials at login.
|
||||
The [synapse LDAP3 auth provider](https://github.com/matrix-org/matrix-synapse-ldap3) only handles one specific flow: validate credentials at login.
|
||||
|
||||
It does not:
|
||||
- Auto-provision user profiles
|
||||
|
@@ -1,5 +1,5 @@
|
||||
# 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)
|
||||
- [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.
|
||||
In a typical configuration, you would end up with something similar to:
|
||||
```yaml
|
||||
trusted_third_party_id_servers:
|
||||
- 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)
|
||||
**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
|
||||
- [Overview](#overview)
|
||||
- [Maintenance](#maintenance)
|
||||
- [Backuo](#backup)
|
||||
- [Operations Guide](#operations-guide)
|
||||
- [Overview](#overview)
|
||||
- [Maintenance](#maintenance)
|
||||
- [Backup](#backup)
|
||||
- [Run](#run)
|
||||
- [Restore](#restore)
|
||||
|
||||
## Overview
|
||||
This document gives various information for the day-to-day management and operations of ma1sd.
|
||||
|
@@ -89,7 +89,7 @@ ldap:
|
||||
#### 3PIDs
|
||||
You can also change the attribute lists for 3PID, like email or phone numbers.
|
||||
|
||||
The following example would overwrite the [default list of attributes](../../src/main/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:
|
||||
```yaml
|
||||
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:
|
||||
- `localpart` will extract and set only the localpart.
|
||||
- `uid` will extract and set only the localpart.
|
||||
- `mxid` will use the ID as-is.
|
||||
|
||||
On each query, the first parameter `?` is set as a string with the corresponding ID format.
|
||||
|
@@ -103,8 +103,8 @@ session:
|
||||
validation:
|
||||
enabled: true
|
||||
unbind:
|
||||
notification:
|
||||
enabled: true
|
||||
notifications: true
|
||||
enabled: true
|
||||
|
||||
# DO NOT COPY/PASTE AS-IS IN YOUR CONFIGURATION
|
||||
# CONFIGURATION EXAMPLE
|
||||
@@ -115,7 +115,7 @@ are allowed to do in terms of 3PID sessions. The policy has a global on/off swit
|
||||
|
||||
---
|
||||
|
||||
`unbind` controls warning notifications for 3PID removal.
|
||||
`unbind` controls warning notifications for 3PID removal. Setting `notifications` for `unbind` to false will prevent unbind emails from sending.
|
||||
|
||||
### Web views
|
||||
Once a user click on a validation link, it is taken to the Identity Server validation page where the token is submitted.
|
||||
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
7
gradle/wrapper/gradle-wrapper.properties
vendored
7
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,7 @@
|
||||
#Thu Jul 04 22:47:59 MSK 2019
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-all.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
301
gradlew
vendored
301
gradlew
vendored
@@ -1,13 +1,13 @@
|
||||
#!/usr/bin/env sh
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright 2015 the original author or authors.
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -15,80 +15,115 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
#
|
||||
# Gradle start up script for POSIX generated by Gradle.
|
||||
#
|
||||
# Important for running:
|
||||
#
|
||||
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||
# noncompliant, but you have some other compliant shell such as ksh or
|
||||
# bash, then to run this script, type that shell name before the whole
|
||||
# command line, like:
|
||||
#
|
||||
# ksh Gradle
|
||||
#
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
# * functions;
|
||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||
# * compound commands having a testable exit status, especially «case»;
|
||||
# * various built-in commands including «command», «set», and «ulimit».
|
||||
#
|
||||
# Important for patching:
|
||||
#
|
||||
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||
#
|
||||
# The "traditional" practice of packing multiple parameters into a
|
||||
# space-separated string is a well documented source of bugs and security
|
||||
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||
# options in "$@", and eventually passing that to Java.
|
||||
#
|
||||
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||
# see the in-line comments for details.
|
||||
#
|
||||
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
app_path=$0
|
||||
|
||||
# Need this for daisy-chained symlinks.
|
||||
while
|
||||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||
[ -h "$app_path" ]
|
||||
do
|
||||
ls=$( ls -ld "$app_path" )
|
||||
link=${ls#*' -> '}
|
||||
case $link in #(
|
||||
/*) app_path=$link ;; #(
|
||||
*) app_path=$APP_HOME$link ;;
|
||||
esac
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
MAX_FD=maximum
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
} >&2
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
} >&2
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
case "$( uname )" in #(
|
||||
CYGWIN* ) cygwin=true ;; #(
|
||||
Darwin* ) darwin=true ;; #(
|
||||
MSYS* | MINGW* ) msys=true ;; #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
CLASSPATH="\\\"\\\""
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
JAVACMD=$JAVA_HOME/bin/java
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
@@ -97,92 +132,120 @@ Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
JAVACMD=java
|
||||
if ! command -v java >/dev/null 2>&1
|
||||
then
|
||||
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=$((i+1))
|
||||
done
|
||||
case $i in
|
||||
(0) set -- ;;
|
||||
(1) set -- "$args0" ;;
|
||||
(2) set -- "$args0" "$args1" ;;
|
||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=$(save "$@")
|
||||
# Collect all arguments for the java command, stacking in reverse order:
|
||||
# * args from the command line
|
||||
# * the main class name
|
||||
# * -classpath
|
||||
# * -D...appname settings
|
||||
# * --module-path (only if needed)
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||
|
||||
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||
cd "$(dirname "$0")"
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
for arg do
|
||||
if
|
||||
case $arg in #(
|
||||
-*) false ;; # don't mess with options #(
|
||||
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||
[ -e "$t" ] ;; #(
|
||||
*) false ;;
|
||||
esac
|
||||
then
|
||||
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||
fi
|
||||
# Roll the args list around exactly as many times as the number of
|
||||
# args, so each arg winds up back in the position where it started, but
|
||||
# possibly modified.
|
||||
#
|
||||
# NB: a `for` loop captures its iteration list before it begins, so
|
||||
# changing the positional parameters here affects neither the number of
|
||||
# iterations, nor the values presented in `arg`.
|
||||
shift # remove old arg
|
||||
set -- "$@" "$arg" # push replacement arg
|
||||
done
|
||||
fi
|
||||
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# and any embedded shellness will be escaped.
|
||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||
# treated as '${Hostname}' itself on the command line.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
if ! command -v xargs >/dev/null 2>&1
|
||||
then
|
||||
die "xargs is not available"
|
||||
fi
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
#
|
||||
# In Bash we could simply go:
|
||||
#
|
||||
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||
# set -- "${ARGS[@]}" "$@"
|
||||
#
|
||||
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||
# character that might be a shell metacharacter, then use eval to reverse
|
||||
# that process (while maintaining the separation between arguments), and wrap
|
||||
# the whole thing up as a single "set" statement.
|
||||
#
|
||||
# This will of course break if any of these variables contains a newline or
|
||||
# an unmatched quote.
|
||||
#
|
||||
|
||||
eval "set -- $(
|
||||
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||
xargs -n1 |
|
||||
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||
tr '\n' ' '
|
||||
)" '"$@"'
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
|
194
gradlew.bat
vendored
194
gradlew.bat
vendored
@@ -1,100 +1,94 @@
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem http://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
@rem SPDX-License-Identifier: Apache-2.0
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@rem This is normally unused
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
set EXIT_CODE=%ERRORLEVEL%
|
||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||
exit /b %EXIT_CODE%
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
|
@@ -1,111 +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: ''
|
||||
|
||||
|
||||
################
|
||||
# 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"
|
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>
|
||||
Homepage: https://github.com/ma1uta/ma1sd
|
||||
Homepage: https://git.cqre.net/cqrenet/mxids.git
|
||||
Description: Federated Matrix Identity Server
|
||||
Architecture: all
|
||||
Section: net
|
||||
|
@@ -1,19 +1,19 @@
|
||||
#!/bin/bash -e
|
||||
|
||||
# Add service account
|
||||
useradd -r ma1sd || true
|
||||
useradd -r mxids || true
|
||||
|
||||
# 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
|
||||
ln -sfT /usr/lib/ma1sd/ma1sd /usr/bin/ma1sd
|
||||
# Create symlink to mxids run script
|
||||
ln -sfT /usr/lib/mxids/mxids /usr/bin/mxids
|
||||
|
||||
# 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
|
||||
if [ -f "%DEB_CONF_FILE%" ]; then
|
||||
systemctl restart ma1sd.service
|
||||
systemctl restart mxids.service
|
||||
fi
|
||||
|
@@ -1,10 +1,10 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Stop running instance if needed
|
||||
systemctl stop ma1sd.service
|
||||
systemctl stop mxids.service
|
||||
|
||||
# Disable service if exists
|
||||
systemctl disable ma1sd.service
|
||||
systemctl disable mxids.service
|
||||
|
||||
# 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"
|
||||
fi
|
||||
|
||||
echo "Starting ma1sd..."
|
||||
echo "Starting mxids..."
|
||||
echo
|
||||
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() {
|
||||
return client().add("r0");
|
||||
return client().add("v3");
|
||||
}
|
||||
|
||||
private StringBuilder path = new StringBuilder();
|
||||
|
@@ -27,7 +27,7 @@ public class ThreePid implements _ThreePid {
|
||||
|
||||
public ThreePid(String medium, String address) {
|
||||
this.medium = medium;
|
||||
this.address = address;
|
||||
this.address = address.toLowerCase();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -375,13 +375,13 @@ public abstract class AMatrixHttpClient implements _MatrixClientRaw {
|
||||
}
|
||||
|
||||
protected HttpUrl.Builder getClientPathBuilder(String... segments) {
|
||||
String[] base = { "client", "r0" };
|
||||
String[] base = { "client", "v3" };
|
||||
segments = ArrayUtils.addAll(base, segments);
|
||||
return getPathBuilder(segments);
|
||||
}
|
||||
|
||||
protected HttpUrl.Builder getMediaPathBuilder(String... segments) {
|
||||
String[] base = { "media", "r0" };
|
||||
String[] base = { "media", "v3" };
|
||||
segments = ArrayUtils.addAll(base, segments);
|
||||
return getPathBuilder(segments);
|
||||
}
|
||||
|
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* matrix-java-sdk - Matrix Client SDK for Java
|
||||
* Copyright (C) 2017 Kamax Sarl
|
||||
*
|
||||
* https://www.kamax.io/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.kamax.matrix.codec;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
public class MxSha256 {
|
||||
|
||||
private MessageDigest md;
|
||||
|
||||
public MxSha256() {
|
||||
try {
|
||||
md = MessageDigest.getInstance("SHA-256");
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public String hash(byte[] data) {
|
||||
return MxBase64.encode(md.digest(data));
|
||||
}
|
||||
|
||||
public String hash(String data) {
|
||||
return hash(data.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
}
|
@@ -23,11 +23,13 @@ package io.kamax.mxisd;
|
||||
import io.kamax.mxisd.config.MatrixConfig;
|
||||
import io.kamax.mxisd.config.MxisdConfig;
|
||||
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.AuthorizationHandler;
|
||||
import io.kamax.mxisd.http.undertow.handler.CheckTermsHandler;
|
||||
import io.kamax.mxisd.http.undertow.handler.InternalInfoHandler;
|
||||
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.as.v1.AsNotFoundHandler;
|
||||
import io.kamax.mxisd.http.undertow.handler.as.v1.AsTransactionHandler;
|
||||
@@ -52,9 +54,11 @@ import io.kamax.mxisd.http.undertow.handler.identity.share.SessionValidationGetH
|
||||
import io.kamax.mxisd.http.undertow.handler.identity.share.SessionValidationPostHandler;
|
||||
import io.kamax.mxisd.http.undertow.handler.identity.share.SignEd25519Handler;
|
||||
import io.kamax.mxisd.http.undertow.handler.identity.share.StoreInviteHandler;
|
||||
import io.kamax.mxisd.http.undertow.handler.identity.v1.*;
|
||||
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.v2.HashDetailsHandler;
|
||||
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.profile.v1.InternalProfileHandler;
|
||||
import io.kamax.mxisd.http.undertow.handler.profile.v1.ProfileHandler;
|
||||
@@ -98,42 +102,35 @@ public class HttpMxisd {
|
||||
public void start() {
|
||||
m.start();
|
||||
|
||||
HttpHandler asUserHandler = SaneHandler.around(new AsUserHandler(m.getAs()));
|
||||
HttpHandler asTxnHandler = SaneHandler.around(new AsTransactionHandler(m.getAs()));
|
||||
HttpHandler asNotFoundHandler = SaneHandler.around(new AsNotFoundHandler(m.getAs()));
|
||||
HttpHandler asUserHandler = sane(new AsUserHandler(m.getAs()));
|
||||
HttpHandler asTxnHandler = sane(new AsTransactionHandler(m.getAs()));
|
||||
HttpHandler asNotFoundHandler = sane(new AsNotFoundHandler(m.getAs()));
|
||||
|
||||
final RoutingHandler handler = Handlers.routing()
|
||||
.add("OPTIONS", "/**", SaneHandler.around(new OptionsHandler()))
|
||||
.add("OPTIONS", "/**", sane(new OptionsHandler()))
|
||||
|
||||
// Status endpoints
|
||||
.get(StatusHandler.Path, SaneHandler.around(new StatusHandler()))
|
||||
.get(VersionHandler.Path, SaneHandler.around(new VersionHandler()))
|
||||
.get(StatusHandler.Path, sane(new StatusHandler()))
|
||||
.get(VersionHandler.Path, sane(new VersionHandler()))
|
||||
|
||||
// Authentication endpoints
|
||||
.get(LoginHandler.Path, SaneHandler.around(new LoginGetHandler(m.getAuth(), m.getHttpClient())))
|
||||
.post(LoginHandler.Path, SaneHandler.around(new LoginPostHandler(m.getAuth())))
|
||||
.post(RestAuthHandler.Path, SaneHandler.around(new RestAuthHandler(m.getAuth())))
|
||||
|
||||
// Account endpoints
|
||||
.post(AccountRegisterHandler.Path, SaneHandler.around(new AccountRegisterHandler(m.getAccMgr())))
|
||||
.get(AccountGetUserInfoHandler.Path,
|
||||
SaneHandler.around(AuthorizationHandler.around(m.getAccMgr(), new AccountGetUserInfoHandler(m.getAccMgr()))))
|
||||
.post(AccountLogoutHandler.Path,
|
||||
SaneHandler.around(AuthorizationHandler.around(m.getAccMgr(), new AccountLogoutHandler(m.getAccMgr()))))
|
||||
.get(LoginHandler.Path, sane(new LoginGetHandler(m.getAuth(), m.getHttpClient())))
|
||||
.post(LoginHandler.Path, sane(new LoginPostHandler(m.getAuth())))
|
||||
.post(RestAuthHandler.Path, sane(new RestAuthHandler(m.getAuth())))
|
||||
|
||||
// Directory endpoints
|
||||
.post(UserDirectorySearchHandler.Path, SaneHandler.around(new UserDirectorySearchHandler(m.getDirectory())))
|
||||
.post(UserDirectorySearchHandler.Path, sane(new UserDirectorySearchHandler(m.getDirectory())))
|
||||
|
||||
// Profile endpoints
|
||||
.get(ProfileHandler.Path, SaneHandler.around(new ProfileHandler(m.getProfile())))
|
||||
.get(InternalProfileHandler.Path, SaneHandler.around(new InternalProfileHandler(m.getProfile())))
|
||||
.get(ProfileHandler.Path, sane(new ProfileHandler(m.getProfile())))
|
||||
.get(InternalProfileHandler.Path, sane(new InternalProfileHandler(m.getProfile())))
|
||||
|
||||
// Registration endpoints
|
||||
.post(Register3pidRequestTokenHandler.Path,
|
||||
SaneHandler.around(new Register3pidRequestTokenHandler(m.getReg(), m.getClientDns(), m.getHttpClient())))
|
||||
sane(new Register3pidRequestTokenHandler(m.getReg(), m.getClientDns(), m.getHttpClient())))
|
||||
|
||||
// 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
|
||||
.get(AsUserHandler.Path, asUserHandler)
|
||||
@@ -145,12 +142,19 @@ public class HttpMxisd {
|
||||
.put("/transactions/{" + AsTransactionHandler.ID + "}", asTxnHandler) // Legacy endpoint
|
||||
|
||||
// Banned endpoints
|
||||
.get(InternalInfoHandler.Path, SaneHandler.around(new InternalInfoHandler()));
|
||||
.get(InternalInfoHandler.Path, sane(new InternalInfoHandler()));
|
||||
keyEndpoints(handler);
|
||||
identityEndpoints(handler);
|
||||
termsEndpoints(handler);
|
||||
hashEndpoints(handler);
|
||||
httpSrv = Undertow.builder().addHttpListener(m.getConfig().getServer().getPort(), "0.0.0.0").setHandler(handler).build();
|
||||
accountEndpoints(handler);
|
||||
|
||||
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();
|
||||
}
|
||||
@@ -193,17 +197,33 @@ public class HttpMxisd {
|
||||
);
|
||||
}
|
||||
|
||||
private void accountEndpoints(RoutingHandler routingHandler) {
|
||||
MatrixConfig matrixConfig = m.getConfig().getMatrix();
|
||||
if (matrixConfig.isV2()) {
|
||||
routingHandler.post(AccountRegisterHandler.Path, sane(new AccountRegisterHandler(m.getAccMgr())));
|
||||
wrapWithTokenAndAuthorizationHandlers(routingHandler, Methods.GET, new AccountGetUserInfoHandler(m.getAccMgr()),
|
||||
AccountGetUserInfoHandler.Path, true);
|
||||
wrapWithTokenAndAuthorizationHandlers(routingHandler, Methods.GET, new AccountLogoutHandler(m.getAccMgr()),
|
||||
AccountLogoutHandler.Path, true);
|
||||
}
|
||||
}
|
||||
|
||||
private void termsEndpoints(RoutingHandler routingHandler) {
|
||||
routingHandler.get(GetTermsHandler.PATH, new GetTermsHandler(m.getConfig().getPolicy()));
|
||||
routingHandler
|
||||
.post(AcceptTermsHandler.PATH, AuthorizationHandler.around(m.getAccMgr(), sane(new AcceptTermsHandler(m.getAccMgr()))));
|
||||
MatrixConfig matrixConfig = m.getConfig().getMatrix();
|
||||
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) {
|
||||
routingHandler
|
||||
.get(HashDetailsHandler.PATH, AuthorizationHandler.around(m.getAccMgr(), sane(new HashDetailsHandler(m.getHashManager()))));
|
||||
routingHandler.post(HashLookupHandler.Path,
|
||||
AuthorizationHandler.around(m.getAccMgr(), sane(new HashLookupHandler(m.getIdentity(), m.getHashManager()))));
|
||||
MatrixConfig matrixConfig = m.getConfig().getMatrix();
|
||||
if (matrixConfig.isV2()) {
|
||||
wrapWithTokenAndAuthorizationHandlers(routingHandler, Methods.GET, new HashDetailsHandler(m.getHashManager()),
|
||||
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) {
|
||||
@@ -216,23 +236,35 @@ public class HttpMxisd {
|
||||
HttpHandler httpHandler) {
|
||||
MatrixConfig matrixConfig = m.getConfig().getMatrix();
|
||||
if (matrixConfig.isV1()) {
|
||||
routingHandler.add(method, apiHandler.getPath(IdentityServiceAPI.V1), httpHandler);
|
||||
routingHandler.add(method, apiHandler.getPath(IdentityServiceAPI.V1), sane(httpHandler));
|
||||
}
|
||||
if (matrixConfig.isV2()) {
|
||||
HttpHandler handlerWithTerms = CheckTermsHandler.around(m.getAccMgr(), httpHandler, getPolicyObjects(apiHandler));
|
||||
HttpHandler wrappedHandler = useAuthorization ? AuthorizationHandler.around(m.getAccMgr(), handlerWithTerms) : handlerWithTerms;
|
||||
routingHandler.add(method, apiHandler.getPath(IdentityServiceAPI.V2), wrappedHandler);
|
||||
wrapWithTokenAndAuthorizationHandlers(routingHandler, method, httpHandler, apiHandler.getPath(IdentityServiceAPI.V2),
|
||||
useAuthorization);
|
||||
}
|
||||
}
|
||||
|
||||
private void wrapWithTokenAndAuthorizationHandlers(RoutingHandler routingHandler, HttpString method, HttpHandler httpHandler,
|
||||
String url, boolean useAuthorization) {
|
||||
List<PolicyConfig.PolicyObject> policyObjects = getPolicyObjects(url);
|
||||
HttpHandler wrappedHandler;
|
||||
if (useAuthorization) {
|
||||
wrappedHandler = policyObjects.isEmpty() ? httpHandler : CheckTermsHandler.around(m.getAccMgr(), httpHandler, policyObjects);
|
||||
wrappedHandler = AuthorizationHandler.around(m.getAccMgr(), wrappedHandler);
|
||||
} else {
|
||||
wrappedHandler = httpHandler;
|
||||
}
|
||||
routingHandler.add(method, url, sane(wrappedHandler));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private List<PolicyConfig.PolicyObject> getPolicyObjects(ApiHandler apiHandler) {
|
||||
private List<PolicyConfig.PolicyObject> getPolicyObjects(String url) {
|
||||
PolicyConfig policyConfig = m.getConfig().getPolicy();
|
||||
List<PolicyConfig.PolicyObject> policies = new ArrayList<>();
|
||||
if (!policyConfig.getPolicies().isEmpty()) {
|
||||
for (PolicyConfig.PolicyObject policy : policyConfig.getPolicies().values()) {
|
||||
for (Pattern pattern : policy.getPatterns()) {
|
||||
if (pattern.matcher(apiHandler.getHandlerPath()).matches()) {
|
||||
if (pattern.matcher(url).matches()) {
|
||||
policies.add(policy);
|
||||
}
|
||||
}
|
||||
@@ -242,6 +274,11 @@ public class HttpMxisd {
|
||||
}
|
||||
|
||||
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.sql.synapse.Synapse;
|
||||
import io.kamax.mxisd.config.MxisdConfig;
|
||||
import io.kamax.mxisd.config.StorageConfig;
|
||||
import io.kamax.mxisd.crypto.CryptoFactory;
|
||||
import io.kamax.mxisd.crypto.KeyManager;
|
||||
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.storage.IStorage;
|
||||
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.HttpClients;
|
||||
|
||||
@@ -66,7 +67,7 @@ public class Mxisd {
|
||||
public static final String Version = StringUtils.defaultIfBlank(Mxisd.class.getPackage().getImplementationVersion(), "UNKNOWN");
|
||||
public static final String Agent = Name + "/" + Version;
|
||||
|
||||
private MxisdConfig cfg;
|
||||
private final MxisdConfig cfg;
|
||||
|
||||
private CloseableHttpClient httpClient;
|
||||
private IRemoteIdentityServerFetcher srvFetcher;
|
||||
@@ -109,7 +110,10 @@ public class Mxisd {
|
||||
IdentityServerUtils.setHttpClient(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());
|
||||
signMgr = CryptoFactory.getSignatureManager(cfg, keyMgr);
|
||||
clientDns = new ClientDnsOverwrite(cfg.getDns().getOverwrite());
|
||||
@@ -125,13 +129,13 @@ public class Mxisd {
|
||||
idStrategy = new RecursivePriorityLookupStrategy(cfg.getLookup(), ThreePidProviders.get(), bridgeFetcher, hashManager);
|
||||
pMgr = new ProfileManager(ProfileProviders.get(), clientDns, httpClient);
|
||||
notifMgr = new NotificationManager(cfg.getNotification(), NotificationHandlers.get());
|
||||
sessMgr = new SessionManager(cfg, store, notifMgr, resolver, httpClient, signMgr);
|
||||
sessMgr = new SessionManager(cfg, store, notifMgr, resolver, signMgr);
|
||||
invMgr = new InvitationManager(cfg, store, idStrategy, keyMgr, signMgr, resolver, notifMgr, pMgr);
|
||||
authMgr = new AuthManager(cfg, AuthProviders.get(), idStrategy, invMgr, clientDns, httpClient);
|
||||
dirMgr = new DirectoryManager(cfg.getDirectory(), clientDns, httpClient, DirectoryProviders.get());
|
||||
regMgr = new RegistrationManager(cfg.getRegister(), httpClient, clientDns, invMgr);
|
||||
asHander = new AppSvcManager(this);
|
||||
accMgr = new AccountManager(store, resolver, getHttpClient(), cfg.getAccountConfig(), cfg.getMatrix());
|
||||
accMgr = new AccountManager(store, resolver, cfg.getAccountConfig(), cfg.getMatrix());
|
||||
}
|
||||
|
||||
public MxisdConfig getConfig() {
|
||||
|
@@ -36,7 +36,7 @@ public class MxisdStandaloneExec {
|
||||
private static final Logger log = LoggerFactory.getLogger("App");
|
||||
|
||||
public static void main(String[] args) {
|
||||
String logLevel = System.getenv("MA1SD_LOG_LEVEL");
|
||||
String logLevel = System.getenv("MXIDS_LOG_LEVEL");
|
||||
if (StringUtils.isNotBlank(logLevel)) {
|
||||
System.setProperty("org.slf4j.simpleLogger.log.io.kamax.mxisd", logLevel);
|
||||
}
|
||||
@@ -44,49 +44,72 @@ public class MxisdStandaloneExec {
|
||||
try {
|
||||
MxisdConfig cfg = null;
|
||||
Iterator<String> argsIt = Arrays.asList(args).iterator();
|
||||
boolean dump = false;
|
||||
boolean exit = false;
|
||||
while (argsIt.hasNext()) {
|
||||
String arg = argsIt.next();
|
||||
if (StringUtils.equalsAny(arg, "-h", "--help", "-?", "--usage")) {
|
||||
System.out.println("Available arguments:" + System.lineSeparator());
|
||||
System.out.println(" -h, --help Show this help message");
|
||||
System.out.println(" --version Print the version then exit");
|
||||
System.out.println(" -c, --config Set the configuration file location");
|
||||
System.out.println(" -v Increase log level (log more info)");
|
||||
System.out.println(" -vv Further increase log level");
|
||||
System.out.println(" ");
|
||||
System.exit(0);
|
||||
} else if (StringUtils.equals(arg, "-v")) {
|
||||
System.setProperty("org.slf4j.simpleLogger.log.io.kamax.mxisd", "debug");
|
||||
} else if (StringUtils.equals(arg, "-vv")) {
|
||||
System.setProperty("org.slf4j.simpleLogger.log.io.kamax.mxisd", "trace");
|
||||
} else if (StringUtils.equalsAny(arg, "-c", "--config")) {
|
||||
String cfgFile = argsIt.next();
|
||||
cfg = YamlConfigLoader.loadFromFile(cfgFile);
|
||||
} else if (StringUtils.equals("--version", arg)) {
|
||||
System.out.println(Mxisd.Version);
|
||||
System.exit(0);
|
||||
} else {
|
||||
System.err.println("Invalid argument: " + arg);
|
||||
System.err.println("Try '--help' for available arguments");
|
||||
System.exit(1);
|
||||
switch (arg) {
|
||||
case "-h":
|
||||
case "--help":
|
||||
case "-?":
|
||||
case "--usage":
|
||||
System.out.println("Available arguments:" + System.lineSeparator());
|
||||
System.out.println(" -h, --help Show this help message");
|
||||
System.out.println(" --version Print the version then exit");
|
||||
System.out.println(" -c, --config Set the configuration file location");
|
||||
System.out.println(" -v Increase log level (log more info)");
|
||||
System.out.println(" -vv Further increase log level");
|
||||
System.out.println(" --dump Dump the full mxids configuration");
|
||||
System.out.println(" --dump-and-exit Dump the full mxids configuration and exit");
|
||||
System.out.println(" ");
|
||||
System.exit(0);
|
||||
return;
|
||||
case "-v":
|
||||
System.setProperty("org.slf4j.simpleLogger.log.io.kamax.mxisd", "debug");
|
||||
break;
|
||||
case "-vv":
|
||||
System.setProperty("org.slf4j.simpleLogger.log.io.kamax.mxisd", "trace");
|
||||
break;
|
||||
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)) {
|
||||
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);
|
||||
|
||||
HttpMxisd mxisd = new HttpMxisd(cfg);
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
||||
mxisd.stop();
|
||||
log.info("ma1sd stopped");
|
||||
log.info("mxids stopped");
|
||||
}));
|
||||
mxisd.start();
|
||||
|
||||
log.info("ma1sd started");
|
||||
log.info("mxids started");
|
||||
} catch (ConfigurationException e) {
|
||||
log.error(e.getDetailedMessage());
|
||||
log.error(e.getMessage());
|
||||
|
@@ -20,7 +20,7 @@
|
||||
|
||||
package io.kamax.mxisd;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
// FIXME consider integrating in matrix-java-sdk?
|
||||
public enum UserIdType {
|
||||
|
@@ -48,6 +48,7 @@ import org.slf4j.LoggerFactory;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
import org.yaml.snakeyaml.introspector.BeanAccess;
|
||||
import org.yaml.snakeyaml.representer.Representer;
|
||||
import org.yaml.snakeyaml.DumperOptions;
|
||||
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
@@ -142,18 +143,18 @@ public class AppSvcManager {
|
||||
String synapseRegFile = cfg.getAppsvc().getRegistration().getSynapse().getFile();
|
||||
|
||||
if (StringUtils.isBlank(synapseRegFile)) {
|
||||
log.info("No synapse registration file path given - skipping generation...");
|
||||
return;
|
||||
log.info("No synapse registration file path given - skipping generation...");
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
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+")));
|
||||
if (StringUtils.equals(lines.get(0), "!!" + SynapseRegistrationYaml.class.getCanonicalName())) {
|
||||
lines.remove(0);
|
||||
|
@@ -25,8 +25,8 @@ import io.kamax.matrix.hs._MatrixRoom;
|
||||
import io.kamax.mxisd.Mxisd;
|
||||
import io.kamax.mxisd.invitation.IThreePidInviteReply;
|
||||
import org.apache.commons.cli.CommandLine;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang.text.StrBuilder;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.text.StrBuilder;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
@@ -25,7 +25,7 @@ import io.kamax.matrix.hs._MatrixRoom;
|
||||
import io.kamax.mxisd.Mxisd;
|
||||
import io.kamax.mxisd.lookup.SingleLookupReply;
|
||||
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 java.util.Optional;
|
||||
|
@@ -34,7 +34,7 @@ import io.kamax.mxisd.invitation.IMatrixIdInvite;
|
||||
import io.kamax.mxisd.invitation.MatrixIdInvite;
|
||||
import io.kamax.mxisd.notification.NotificationManager;
|
||||
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.LoggerFactory;
|
||||
|
||||
@@ -144,7 +144,13 @@ public class MembershipEventProcessor implements EventTypeProcessor {
|
||||
.collect(Collectors.toList());
|
||||
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());
|
||||
Map<String, String> properties = new HashMap<>();
|
||||
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.PingCommandProcessor;
|
||||
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.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@@ -9,14 +9,15 @@ import io.kamax.mxisd.config.PolicyConfig;
|
||||
import io.kamax.mxisd.exception.BadRequestException;
|
||||
import io.kamax.mxisd.exception.InvalidCredentialsException;
|
||||
import io.kamax.mxisd.exception.NotFoundException;
|
||||
import io.kamax.mxisd.http.undertow.handler.auth.v1.LoginGetHandler;
|
||||
import io.kamax.mxisd.matrix.HomeserverFederationResolver;
|
||||
import io.kamax.mxisd.matrix.HomeserverVerifier;
|
||||
import io.kamax.mxisd.storage.IStorage;
|
||||
import io.kamax.mxisd.storage.ormlite.dao.AccountDao;
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -33,15 +34,12 @@ public class AccountManager {
|
||||
|
||||
private final IStorage storage;
|
||||
private final HomeserverFederationResolver resolver;
|
||||
private final CloseableHttpClient httpClient;
|
||||
private final AccountConfig accountConfig;
|
||||
private final MatrixConfig matrixConfig;
|
||||
|
||||
public AccountManager(IStorage storage, HomeserverFederationResolver resolver,
|
||||
CloseableHttpClient httpClient, AccountConfig accountConfig, MatrixConfig matrixConfig) {
|
||||
public AccountManager(IStorage storage, HomeserverFederationResolver resolver, AccountConfig accountConfig, MatrixConfig matrixConfig) {
|
||||
this.storage = storage;
|
||||
this.resolver = resolver;
|
||||
this.httpClient = httpClient;
|
||||
this.accountConfig = accountConfig;
|
||||
this.matrixConfig = matrixConfig;
|
||||
}
|
||||
@@ -57,7 +55,7 @@ public class AccountManager {
|
||||
|
||||
String token = UUID.randomUUID().toString();
|
||||
AccountDao account = new AccountDao(openIdToken.getAccessToken(), openIdToken.getTokenType(),
|
||||
openIdToken.getMatrixServerName(), openIdToken.getExpiredIn(),
|
||||
openIdToken.getMatrixServerName(), openIdToken.getExpiresIn(),
|
||||
Instant.now().getEpochSecond(), userId, token);
|
||||
storage.insertToken(account);
|
||||
|
||||
@@ -67,24 +65,32 @@ public class AccountManager {
|
||||
}
|
||||
|
||||
private String getUserId(OpenIdToken openIdToken) {
|
||||
String homeserverURL = resolver.resolve(openIdToken.getMatrixServerName()).toString();
|
||||
LOGGER.info("Domain resolved: {} => {}", openIdToken.getMatrixServerName(), homeserverURL);
|
||||
String matrixServerName = openIdToken.getMatrixServerName();
|
||||
HomeserverFederationResolver.HomeserverTarget homeserverTarget = resolver.resolve(matrixServerName);
|
||||
String homeserverURL = homeserverTarget.getUrl().toString();
|
||||
LOGGER.info("Domain resolved: {} => {}", matrixServerName, homeserverURL);
|
||||
HttpGet getUserInfo = new HttpGet(
|
||||
homeserverURL + "/_matrix/federation/v1/openid/userinfo?access_token=" + openIdToken.getAccessToken());
|
||||
String userId;
|
||||
try (CloseableHttpResponse response = httpClient.execute(getUserInfo)) {
|
||||
int statusCode = response.getStatusLine().getStatusCode();
|
||||
if (statusCode == HttpStatus.SC_OK) {
|
||||
String content = EntityUtils.toString(response.getEntity());
|
||||
LOGGER.trace("Response: {}", content);
|
||||
JsonObject body = GsonUtil.parseObj(content);
|
||||
userId = GsonUtil.getStringOrThrow(body, "sub");
|
||||
} else {
|
||||
LOGGER.error("Wrong response status: {}", statusCode);
|
||||
try (CloseableHttpClient httpClient = HttpClients.custom()
|
||||
.setSSLHostnameVerifier(new HomeserverVerifier(homeserverTarget.getDomain())).build()) {
|
||||
try (CloseableHttpResponse response = httpClient.execute(getUserInfo)) {
|
||||
int statusCode = response.getStatusLine().getStatusCode();
|
||||
if (statusCode == HttpStatus.SC_OK) {
|
||||
String content = EntityUtils.toString(response.getEntity());
|
||||
LOGGER.trace("Response: {}", content);
|
||||
JsonObject body = GsonUtil.parseObj(content);
|
||||
userId = GsonUtil.getStringOrThrow(body, "sub");
|
||||
} else {
|
||||
LOGGER.error("Wrong response status: {}", statusCode);
|
||||
throw new InvalidCredentialsException();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LOGGER.error("Unable to get user info.", e);
|
||||
throw new InvalidCredentialsException();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LOGGER.error("Unable to get user info.", e);
|
||||
LOGGER.error("Unable to create a connection to host: " + homeserverURL, e);
|
||||
throw new InvalidCredentialsException();
|
||||
}
|
||||
|
||||
|
@@ -45,7 +45,7 @@ import io.kamax.mxisd.lookup.ThreePidMapping;
|
||||
import io.kamax.mxisd.lookup.strategy.LookupStrategy;
|
||||
import io.kamax.mxisd.util.RestClientUtils;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
@@ -140,7 +140,7 @@ public class AuthManager {
|
||||
}
|
||||
|
||||
try {
|
||||
MatrixID.asValid(mxId);
|
||||
MatrixID.asAcceptable(mxId);
|
||||
} catch (IllegalArgumentException e) {
|
||||
log.warn("The returned User ID {} is not a valid Matrix ID. Login might fail at the Homeserver level", mxId);
|
||||
}
|
||||
|
@@ -1,14 +1,20 @@
|
||||
package io.kamax.mxisd.auth;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
public class OpenIdToken {
|
||||
|
||||
@SerializedName("access_token")
|
||||
private String accessToken;
|
||||
|
||||
@SerializedName("token_type")
|
||||
private String tokenType;
|
||||
|
||||
@SerializedName("matrix_server_name")
|
||||
private String matrixServerName;
|
||||
|
||||
private long expiredIn;
|
||||
@SerializedName("expires_in")
|
||||
private long expiresIn;
|
||||
|
||||
public String getAccessToken() {
|
||||
return accessToken;
|
||||
@@ -34,11 +40,11 @@ public class OpenIdToken {
|
||||
this.matrixServerName = matrixServerName;
|
||||
}
|
||||
|
||||
public long getExpiredIn() {
|
||||
return expiredIn;
|
||||
public long getExpiresIn() {
|
||||
return expiresIn;
|
||||
}
|
||||
|
||||
public void setExpiredIn(long expiredIn) {
|
||||
this.expiredIn = expiredIn;
|
||||
public void setExpiresIn(long expiresIn) {
|
||||
this.expiresIn = expiresIn;
|
||||
}
|
||||
}
|
||||
|
@@ -173,6 +173,10 @@ public class ExecIdentityStore extends ExecStore implements IThreePidProvider {
|
||||
|
||||
@Override
|
||||
public Iterable<ThreePidMapping> populateHashes() {
|
||||
if (!cfg.isHashLookup()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
Processor<List<ThreePidMapping>> p = new Processor<>();
|
||||
p.withConfig(cfg.getLookup().getBulk());
|
||||
|
||||
|
@@ -27,7 +27,7 @@ import io.kamax.mxisd.config.ExecConfig;
|
||||
import io.kamax.mxisd.profile.JsonProfileRequest;
|
||||
import io.kamax.mxisd.profile.JsonProfileResult;
|
||||
import io.kamax.mxisd.profile.ProfileProvider;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.Collections;
|
||||
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;
|
||||
|
||||
import com.google.firebase.auth.UserInfo;
|
||||
import com.google.i18n.phonenumbers.NumberParseException;
|
||||
import com.google.i18n.phonenumbers.PhoneNumberUtil;
|
||||
import io.kamax.matrix.ThreePid;
|
||||
import io.kamax.matrix.ThreePidMedium;
|
||||
import com.google.firebase.auth.FirebaseAuth;
|
||||
import com.google.firebase.auth.FirebaseAuthException;
|
||||
import com.google.firebase.auth.FirebaseToken;
|
||||
import com.google.firebase.auth.UserRecord;
|
||||
import io.kamax.matrix._MatrixID;
|
||||
import io.kamax.mxisd.UserIdType;
|
||||
import io.kamax.mxisd.auth.provider.AuthenticatorProvider;
|
||||
import io.kamax.mxisd.auth.provider.BackendAuthResult;
|
||||
import io.kamax.mxisd.config.FirebaseConfig;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
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) {
|
||||
this(cfg.isEnabled(), cfg.getCredentials(), cfg.getDatabase());
|
||||
public GoogleFirebaseAuthenticator(FirebaseConfig config) {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
public GoogleFirebaseAuthenticator(boolean isEnabled, String credsPath, String db) {
|
||||
super(isEnabled, "AuthenticationProvider", credsPath, db);
|
||||
}
|
||||
|
||||
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
|
||||
public boolean isEnabled() {
|
||||
return this.config.isEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BackendAuthResult authenticate(_MatrixID mxid, String password) {
|
||||
if (!isEnabled()) {
|
||||
throw new IllegalStateException();
|
||||
log.warn("Firebase authenticator is disabled.");
|
||||
return BackendAuthResult.failure();
|
||||
}
|
||||
|
||||
log.info("Trying to authenticate {}", mxid);
|
||||
|
||||
final BackendAuthResult result = BackendAuthResult.failure();
|
||||
|
||||
String localpart = mxid.getLocalPart();
|
||||
CountDownLatch l = new CountDownLatch(1);
|
||||
getFirebase().verifyIdToken(password).addOnSuccessListener(token -> {
|
||||
CompletableFuture<BackendAuthResult> resultFuture = new CompletableFuture<>();
|
||||
executor.submit(() -> {
|
||||
try {
|
||||
if (!StringUtils.equals(localpart, token.getUid())) {
|
||||
log.info("Failure to authenticate {}: Matrix ID localpart '{}' does not match Firebase UID '{}'", mxid, localpart, token.getUid());
|
||||
result.fail();
|
||||
FirebaseToken decodedToken = FirebaseAuth.getInstance().verifyIdToken(password);
|
||||
if (!mxid.getLocalPart().equals(decodedToken.getUid())) {
|
||||
log.warn("UID mismatch for user {}", mxid);
|
||||
resultFuture.complete(BackendAuthResult.failure());
|
||||
return;
|
||||
}
|
||||
|
||||
result.succeed(mxid.getId(), UserIdType.MatrixID.getId(), token.getName());
|
||||
log.info("{} was successfully authenticated", mxid);
|
||||
log.info("Fetching profile for {}", mxid);
|
||||
CountDownLatch userRecordLatch = new CountDownLatch(1);
|
||||
getFirebase().getUser(token.getUid()).addOnSuccessListener(user -> {
|
||||
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();
|
||||
// Assuming you have a method to convert Firebase user info into BackendAuthResult
|
||||
resultFuture.complete(convertToAuthResult(decodedToken));
|
||||
} catch (FirebaseAuthException e) {
|
||||
log.error("Failed to authenticate user {}: {}", mxid, e.getMessage(), e);
|
||||
resultFuture.complete(BackendAuthResult.failure());
|
||||
}
|
||||
});
|
||||
|
||||
waitOnLatch(result, l, "Firebase auth check");
|
||||
return result;
|
||||
try {
|
||||
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;
|
||||
|
||||
import com.google.auth.oauth2.GoogleCredentials;
|
||||
import com.google.firebase.FirebaseApp;
|
||||
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.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);
|
||||
|
||||
private boolean isEnabled;
|
||||
private FirebaseAuth fbAuth;
|
||||
protected FirebaseDatabase fbDb;
|
||||
|
||||
GoogleFirebaseBackend(boolean isEnabled, String name, String credsPath, String db) {
|
||||
this.isEnabled = isEnabled;
|
||||
if (!isEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
public GoogleFirebaseBackend(boolean isEnabled, String backendName, String credsPath, String db) {
|
||||
this.enabled = isEnabled;
|
||||
this.backendName = backendName;
|
||||
this.credentialsPath = credsPath;
|
||||
this.databaseUrl = db;
|
||||
if (isEnabled) {
|
||||
try {
|
||||
initializeFirebase();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to initialize Firebase", e);
|
||||
}
|
||||
} else {
|
||||
return FirebaseCredentials.applicationDefault();
|
||||
}
|
||||
}
|
||||
|
||||
private FirebaseOptions getOpts(String credsPath, String db) throws IOException {
|
||||
if (StringUtils.isBlank(db)) {
|
||||
throw new IllegalArgumentException("Firebase database is not configured");
|
||||
}
|
||||
private void initializeFirebase() throws IOException {
|
||||
FileInputStream serviceAccount = new FileInputStream(credentialsPath);
|
||||
|
||||
return new FirebaseOptions.Builder()
|
||||
.setCredential(getCreds(credsPath))
|
||||
.setDatabaseUrl(db)
|
||||
FirebaseOptions options = new FirebaseOptions.Builder()
|
||||
.setCredentials(GoogleCredentials.fromStream(serviceAccount))
|
||||
.setDatabaseUrl(databaseUrl)
|
||||
.build();
|
||||
|
||||
if (FirebaseApp.getApps().isEmpty()) { // Check if Firebase has been initialized already
|
||||
FirebaseApp.initializeApp(options);
|
||||
}
|
||||
}
|
||||
|
||||
FirebaseAuth getFirebase() {
|
||||
return fbAuth;
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return isEnabled;
|
||||
}
|
||||
|
||||
// Additional methods for GoogleFirebaseBackend
|
||||
}
|
||||
|
@@ -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;
|
||||
|
||||
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.ThreePidMedium;
|
||||
import io.kamax.mxisd.config.MxisdConfig;
|
||||
@@ -36,25 +13,22 @@ import org.slf4j.LoggerFactory;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
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;
|
||||
|
||||
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) {
|
||||
super(isEnabled, "ThreePidProvider", credsPath, db);
|
||||
this.domain = domain;
|
||||
}
|
||||
|
||||
private String getMxid(UserRecord record) {
|
||||
return MatrixID.asAcceptable(record.getUid(), domain).getId();
|
||||
private String getMxid(String uid) {
|
||||
// Mock UID to MXID conversion
|
||||
return MatrixID.asAcceptable(uid, domain).getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -67,71 +41,34 @@ public class GoogleFirebaseProvider extends GoogleFirebaseBackend implements ITh
|
||||
return 25;
|
||||
}
|
||||
|
||||
private void waitOnLatch(CountDownLatch l) {
|
||||
try {
|
||||
l.await(30, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
log.warn("Interrupted while waiting for Firebase auth check");
|
||||
}
|
||||
}
|
||||
private Optional<String> findInternal(String medium, String address) {
|
||||
CompletableFuture<Optional<String>> future = new CompletableFuture<>();
|
||||
|
||||
private Optional<UserRecord> findInternal(String medium, String address) {
|
||||
final UserRecord[] r = new UserRecord[1];
|
||||
CountDownLatch l = new CountDownLatch(1);
|
||||
// Directly complete with empty to simulate no user found
|
||||
future.complete(Optional.empty());
|
||||
|
||||
OnSuccessListener<UserRecord> success = result -> {
|
||||
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]);
|
||||
return future.join(); // Using join to avoid handling InterruptedException
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<SingleLookupReply> find(SingleLookupRequest request) {
|
||||
Optional<UserRecord> urOpt = findInternal(request.getType(), request.getThreePid());
|
||||
return urOpt.map(userRecord -> new SingleLookupReply(request, getMxid(userRecord)));
|
||||
|
||||
Optional<String> uidOpt = findInternal(request.getType(), request.getThreePid());
|
||||
return uidOpt.map(uid -> new SingleLookupReply(request, getMxid(uid)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ThreePidMapping> populate(List<ThreePidMapping> mappings) {
|
||||
List<ThreePidMapping> results = new ArrayList<>();
|
||||
mappings.parallelStream().forEach(o -> {
|
||||
Optional<UserRecord> urOpt = findInternal(o.getMedium(), o.getValue());
|
||||
if (urOpt.isPresent()) {
|
||||
mappings.forEach(o -> {
|
||||
Optional<String> uidOpt = findInternal(o.getMedium(), o.getValue());
|
||||
uidOpt.ifPresent(uid -> {
|
||||
ThreePidMapping result = new ThreePidMapping();
|
||||
result.setMedium(o.getMedium());
|
||||
result.setValue(o.getValue());
|
||||
result.setMxid(getMxid(urOpt.get()));
|
||||
result.setMxid(getMxid(uid));
|
||||
results.add(result);
|
||||
}
|
||||
});
|
||||
});
|
||||
return results;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -32,7 +32,7 @@ import io.kamax.mxisd.config.MatrixConfig;
|
||||
import io.kamax.mxisd.config.ldap.LdapConfig;
|
||||
import io.kamax.mxisd.exception.InternalServerError;
|
||||
import io.kamax.mxisd.util.GsonUtil;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
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.EntryCursor;
|
||||
@@ -54,6 +54,8 @@ public class LdapAuthProvider extends LdapBackend implements AuthenticatorProvid
|
||||
|
||||
private transient final Logger log = LoggerFactory.getLogger(LdapAuthProvider.class);
|
||||
|
||||
public static final char[] CHARACTERS_TO_ESCAPE = ",#+<>;\"=*\\\\".toCharArray();
|
||||
|
||||
private PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
|
||||
|
||||
public LdapAuthProvider(LdapConfig cfg, MatrixConfig mxCfg) {
|
||||
@@ -94,7 +96,8 @@ public class LdapAuthProvider extends LdapBackend implements AuthenticatorProvid
|
||||
return BackendAuthResult.failure();
|
||||
}
|
||||
|
||||
String userFilter = "(" + getUidAtt() + "=" + userFilterValue + ")";
|
||||
String filteredValue = escape(userFilterValue);
|
||||
String userFilter = "(" + getUidAtt() + "=" + filteredValue + ")";
|
||||
userFilter = buildWithFilter(userFilter, getCfg().getAuth().getFilter());
|
||||
|
||||
Set<String> attributes = new HashSet<>();
|
||||
@@ -162,8 +165,21 @@ public class LdapAuthProvider extends LdapBackend implements AuthenticatorProvid
|
||||
log.info("No match were found for {}", mxid);
|
||||
return BackendAuthResult.failure();
|
||||
} catch (LdapException | IOException | CursorException e) {
|
||||
log.error("Unable to invoke query request: ", 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;
|
||||
|
||||
import io.kamax.matrix.MatrixID;
|
||||
import io.kamax.matrix._MatrixID;
|
||||
import io.kamax.mxisd.config.MatrixConfig;
|
||||
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.AttributeUtils;
|
||||
import org.apache.directory.api.ldap.model.entry.Entry;
|
||||
@@ -116,10 +117,20 @@ public abstract class LdapBackend {
|
||||
|
||||
public String buildMatrixIdFromUid(String uid) {
|
||||
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)) {
|
||||
return "@" + uid + ":" + mxCfg.getDomain();
|
||||
if(getCfg().isActiveDirectory()) {
|
||||
localpart = new UPN(uid.toLowerCase()).getMXID();
|
||||
}
|
||||
|
||||
return "@" + localpart + ":" + mxCfg.getDomain();
|
||||
} else if (StringUtils.equals(MATRIX_ID, uidType)) {
|
||||
return uid;
|
||||
return localpart;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Bind type " + uidType + " is not supported");
|
||||
}
|
||||
@@ -128,6 +139,10 @@ public abstract class LdapBackend {
|
||||
public String buildUidFromMatrixId(_MatrixID mxId) {
|
||||
String uidType = getCfg().getAttribute().getUid().getType();
|
||||
if (StringUtils.equals(UID, uidType)) {
|
||||
if(getCfg().isActiveDirectory()) {
|
||||
return new UPN(mxId).getUPN();
|
||||
}
|
||||
|
||||
return mxId.getLocalPart();
|
||||
} else if (StringUtils.equals(MATRIX_ID, uidType)) {
|
||||
return mxId.getId();
|
||||
@@ -169,4 +184,58 @@ public abstract class LdapBackend {
|
||||
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.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class LdapThreePidProvider extends LdapBackend implements IThreePidProvider {
|
||||
|
||||
@@ -137,4 +140,65 @@ public class LdapThreePidProvider extends LdapBackend implements IThreePidProvid
|
||||
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.provider.IThreePidProvider;
|
||||
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.LoggerFactory;
|
||||
|
||||
@@ -174,6 +174,10 @@ public class MemoryIdentityStore implements AuthenticatorProvider, DirectoryProv
|
||||
|
||||
@Override
|
||||
public Iterable<ThreePidMapping> populateHashes() {
|
||||
if (!cfg.isHashEnabled()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return cfg.getIdentities().stream()
|
||||
.map(mic -> mic.getThreepids().stream().map(mtp -> new ThreePidMapping(mtp.getMedium(), mtp.getAddress(), mic.getUsername())))
|
||||
.flatMap(s -> s).collect(
|
||||
|
@@ -27,7 +27,7 @@ import io.kamax.mxisd.lookup.SingleLookupReply;
|
||||
import io.kamax.mxisd.lookup.SingleLookupRequest;
|
||||
import io.kamax.mxisd.lookup.ThreePidMapping;
|
||||
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.LoggerFactory;
|
||||
|
||||
@@ -107,14 +107,16 @@ public abstract class SqlThreePidProvider implements IThreePidProvider {
|
||||
|
||||
@Override
|
||||
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.");
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
log.debug("Uses query to match users: {}", query);
|
||||
List<ThreePidMapping> result = new ArrayList<>();
|
||||
try (Connection connection = pool.get()) {
|
||||
PreparedStatement statement = connection.prepareStatement(cfg.getLookup().getQuery());
|
||||
PreparedStatement statement = connection.prepareStatement(query);
|
||||
try (ResultSet resultSet = statement.executeQuery()) {
|
||||
while (resultSet.next()) {
|
||||
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.exception.InternalServerError;
|
||||
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.LoggerFactory;
|
||||
|
||||
|
@@ -29,23 +29,27 @@ import java.util.Optional;
|
||||
|
||||
public class Synapse {
|
||||
|
||||
private SqlConnectionPool pool;
|
||||
private final SqlConnectionPool pool;
|
||||
private final SynapseSqlProviderConfig providerConfig;
|
||||
|
||||
public Synapse(SynapseSqlProviderConfig sqlCfg) {
|
||||
this.pool = new SqlConnectionPool(sqlCfg);
|
||||
providerConfig = sqlCfg;
|
||||
}
|
||||
|
||||
public Optional<String> getRoomName(String id) {
|
||||
return pool.withConnFunction(conn -> {
|
||||
PreparedStatement stmt = conn.prepareStatement(SynapseQueries.getRoomName());
|
||||
stmt.setString(1, id);
|
||||
ResultSet rSet = stmt.executeQuery();
|
||||
if (!rSet.next()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
String query = providerConfig.isLegacyRoomNames() ? SynapseQueries.getLegacyRoomName() : SynapseQueries.getRoomName();
|
||||
|
||||
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;
|
||||
|
||||
import io.kamax.mxisd.exception.ConfigurationException;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
public class SynapseQueries {
|
||||
|
||||
@@ -51,7 +51,7 @@ public class SynapseQueries {
|
||||
if (StringUtils.equals("sqlite", type)) {
|
||||
return "select " + getUserId(type, domain) + ", displayname from profiles p where displayname like ?";
|
||||
} 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 {
|
||||
throw new ConfigurationException("Invalid Synapse SQL type: " + type);
|
||||
}
|
||||
@@ -72,7 +72,10 @@ public class SynapseQueries {
|
||||
}
|
||||
|
||||
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.auth.provider.AuthenticatorProvider;
|
||||
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.LoggerFactory;
|
||||
|
||||
|
@@ -25,7 +25,7 @@ import io.kamax.matrix.json.GsonUtil;
|
||||
import io.kamax.matrix.json.InvalidJsonException;
|
||||
import io.kamax.mxisd.config.wordpress.WordpressConfig;
|
||||
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.HttpGet;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
|
@@ -0,0 +1,5 @@
|
||||
package io.kamax.mxisd.config;
|
||||
|
||||
public interface DatabaseStorageConfig {
|
||||
String getDatabase();
|
||||
}
|
@@ -0,0 +1,30 @@
|
||||
package io.kamax.mxisd.config;
|
||||
|
||||
public class DurationDeserializer {
|
||||
|
||||
public long deserialize(String argument) {
|
||||
long duration = 0L;
|
||||
for (String part : argument.split(" ")) {
|
||||
String unit = part.substring(part.length() - 1);
|
||||
long value = Long.parseLong(part.substring(0, part.length() - 1));
|
||||
switch (unit) {
|
||||
case "s":
|
||||
duration += value;
|
||||
break;
|
||||
case "m":
|
||||
duration += value * 60;
|
||||
break;
|
||||
case "h":
|
||||
duration += value * 60 * 60;
|
||||
break;
|
||||
case "d":
|
||||
duration += value * 60 * 60 * 24;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException(String.format("Unknown duration unit: %s", unit));
|
||||
}
|
||||
}
|
||||
|
||||
return duration;
|
||||
}
|
||||
}
|
@@ -309,6 +309,7 @@ public class ExecConfig {
|
||||
private Boolean enabled;
|
||||
private int priority;
|
||||
private Lookup lookup = new Lookup();
|
||||
private boolean hashLookup = false;
|
||||
|
||||
public Boolean isEnabled() {
|
||||
return enabled;
|
||||
@@ -334,6 +335,13 @@ public class ExecConfig {
|
||||
this.lookup = lookup;
|
||||
}
|
||||
|
||||
public boolean isHashLookup() {
|
||||
return hashLookup;
|
||||
}
|
||||
|
||||
public void setHashLookup(boolean hashLookup) {
|
||||
this.hashLookup = hashLookup;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Profile {
|
||||
|
@@ -5,45 +5,58 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class HashingConfig {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(HashingConfig.class);
|
||||
|
||||
private boolean enabled = false;
|
||||
private int pepperLength = 10;
|
||||
private int pepperLength = 20;
|
||||
private RotationPolicyEnum rotationPolicy;
|
||||
private HashStorageEnum hashStorageType;
|
||||
private long delay = 10;
|
||||
private HashStorageEnum hashStorageType = HashStorageEnum.in_memory;
|
||||
private String delay = "10s";
|
||||
private transient long delayInSeconds = 10;
|
||||
private int requests = 10;
|
||||
private List<Algorithm> algorithms = new ArrayList<>();
|
||||
|
||||
public void build() {
|
||||
public void build(MatrixConfig matrixConfig) {
|
||||
if (isEnabled()) {
|
||||
LOGGER.info("--- Hash configuration ---");
|
||||
LOGGER.info(" Pepper length: {}", getPepperLength());
|
||||
LOGGER.info(" Rotation policy: {}", getRotationPolicy());
|
||||
LOGGER.info(" Hash storage type: {}", getHashStorageType());
|
||||
if (RotationPolicyEnum.PER_SECONDS == rotationPolicy) {
|
||||
LOGGER.info(" Rotation delay: {}", delay);
|
||||
Objects.requireNonNull(getHashStorageType(), "Storage type must be specified");
|
||||
if (RotationPolicyEnum.per_seconds == getRotationPolicy()) {
|
||||
setDelayInSeconds(new DurationDeserializer().deserialize(getDelay()));
|
||||
LOGGER.info(" Rotation delay: {}", getDelay());
|
||||
LOGGER.info(" Rotation delay in seconds: {}", getDelayInSeconds());
|
||||
}
|
||||
if (RotationPolicyEnum.per_requests == getRotationPolicy()) {
|
||||
LOGGER.info(" Rotation after requests: {}", getRequests());
|
||||
}
|
||||
LOGGER.info(" Algorithms: {}", getAlgorithms());
|
||||
} else {
|
||||
if (matrixConfig.isV2()) {
|
||||
LOGGER.warn("V2 enabled without the hash configuration.");
|
||||
}
|
||||
LOGGER.info("Hash configuration disabled, used only `none` pepper.");
|
||||
}
|
||||
}
|
||||
|
||||
public enum Algorithm {
|
||||
NONE,
|
||||
SHA256
|
||||
none,
|
||||
sha256
|
||||
}
|
||||
|
||||
public enum RotationPolicyEnum {
|
||||
PER_REQUESTS,
|
||||
PER_SECONDS
|
||||
per_requests,
|
||||
per_seconds
|
||||
}
|
||||
|
||||
public enum HashStorageEnum {
|
||||
IN_MEMORY,
|
||||
SQL
|
||||
in_memory,
|
||||
sql
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
@@ -78,14 +91,30 @@ public class HashingConfig {
|
||||
this.hashStorageType = hashStorageType;
|
||||
}
|
||||
|
||||
public long getDelay() {
|
||||
public String getDelay() {
|
||||
return delay;
|
||||
}
|
||||
|
||||
public void setDelay(long delay) {
|
||||
public void setDelay(String delay) {
|
||||
this.delay = delay;
|
||||
}
|
||||
|
||||
public long getDelayInSeconds() {
|
||||
return delayInSeconds;
|
||||
}
|
||||
|
||||
public void setDelayInSeconds(long delayInSeconds) {
|
||||
this.delayInSeconds = delayInSeconds;
|
||||
}
|
||||
|
||||
public int getRequests() {
|
||||
return requests;
|
||||
}
|
||||
|
||||
public void setRequests(int requests) {
|
||||
this.requests = requests;
|
||||
}
|
||||
|
||||
public List<Algorithm> getAlgorithms() {
|
||||
return algorithms;
|
||||
}
|
||||
|
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 long timer = 5;
|
||||
private PeriodDimension period = PeriodDimension.minutes;
|
||||
|
||||
public boolean isRecursive() {
|
||||
return recursive;
|
||||
@@ -84,6 +85,13 @@ public class InvitationConfig {
|
||||
this.timer = timer;
|
||||
}
|
||||
|
||||
public PeriodDimension getPeriod() {
|
||||
return period;
|
||||
}
|
||||
|
||||
public void setPeriod(PeriodDimension period) {
|
||||
this.period = period;
|
||||
}
|
||||
}
|
||||
|
||||
public static class SenderPolicy {
|
||||
@@ -115,6 +123,7 @@ public class InvitationConfig {
|
||||
private Expiration expiration = new Expiration();
|
||||
private Resolution resolution = new Resolution();
|
||||
private Policies policy = new Policies();
|
||||
private boolean fullDisplayName = false;
|
||||
|
||||
public Expiration getExpiration() {
|
||||
return expiration;
|
||||
@@ -140,11 +149,26 @@ public class InvitationConfig {
|
||||
this.policy = policy;
|
||||
}
|
||||
|
||||
public boolean isFullDisplayName() {
|
||||
return fullDisplayName;
|
||||
}
|
||||
|
||||
public void setFullDisplayName(boolean fullDisplayName) {
|
||||
this.fullDisplayName = fullDisplayName;
|
||||
}
|
||||
|
||||
public void build() {
|
||||
log.info("--- Invite config ---");
|
||||
log.info("Expiration: {}", GsonUtil.get().toJson(getExpiration()));
|
||||
log.info("Resolution: {}", GsonUtil.get().toJson(getResolution()));
|
||||
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;
|
||||
|
||||
import io.kamax.mxisd.exception.ConfigurationException;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
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.mxisd.exception.ConfigurationException;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -64,7 +64,7 @@ public class MatrixConfig {
|
||||
private String domain;
|
||||
private Identity identity = new Identity();
|
||||
private boolean v1 = true;
|
||||
private boolean v2 = true;
|
||||
private boolean v2 = false;
|
||||
|
||||
public String getDomain() {
|
||||
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.notification.NotificationConfig;
|
||||
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.LoggerFactory;
|
||||
|
||||
@@ -117,6 +117,8 @@ public class MxisdConfig {
|
||||
private WordpressConfig wordpress = new WordpressConfig();
|
||||
private PolicyConfig policy = new PolicyConfig();
|
||||
private HashingConfig hashing = new HashingConfig();
|
||||
private LoggingConfig logging = new LoggingConfig();
|
||||
private InternalAPIConfig internal = new InternalAPIConfig();
|
||||
|
||||
public AppServiceConfig getAppsvc() {
|
||||
return appsvc;
|
||||
@@ -342,6 +344,14 @@ public class MxisdConfig {
|
||||
this.hashing = hashing;
|
||||
}
|
||||
|
||||
public LoggingConfig getLogging() {
|
||||
return logging;
|
||||
}
|
||||
|
||||
public void setLogging(LoggingConfig logging) {
|
||||
this.logging = logging;
|
||||
}
|
||||
|
||||
public MxisdConfig inMemory() {
|
||||
getKey().setPath(":memory:");
|
||||
getStorage().getProvider().getSqlite().setDatabase(":memory:");
|
||||
@@ -349,7 +359,17 @@ public class MxisdConfig {
|
||||
return this;
|
||||
}
|
||||
|
||||
public InternalAPIConfig getInternal() {
|
||||
return internal;
|
||||
}
|
||||
|
||||
public void setInternal(InternalAPIConfig internal) {
|
||||
this.internal = internal;
|
||||
}
|
||||
|
||||
public MxisdConfig build() {
|
||||
getLogging().build();
|
||||
|
||||
if (StringUtils.isBlank(getServer().getName())) {
|
||||
getServer().setName(getMatrix().getDomain());
|
||||
log.debug("server.name is empty, using matrix.domain");
|
||||
@@ -359,6 +379,7 @@ public class MxisdConfig {
|
||||
getAuth().build();
|
||||
getAccountConfig().build();
|
||||
getDirectory().build();
|
||||
getDns().build();
|
||||
getExec().build();
|
||||
getFirebase().build();
|
||||
getForward().build();
|
||||
@@ -381,7 +402,8 @@ public class MxisdConfig {
|
||||
getView().build();
|
||||
getWordpress().build();
|
||||
getPolicy().build();
|
||||
getHashing().build();
|
||||
getHashing().build(getMatrix());
|
||||
getInternal().build();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
@@ -100,10 +100,12 @@ public class PolicyConfig {
|
||||
policyObjectItem.getValue().getPatterns().add(Pattern.compile(regexp));
|
||||
}
|
||||
sb.append(" terms:\n");
|
||||
for (Map.Entry<String, TermObject> termItem : policyObject.getTerms().entrySet()) {
|
||||
sb.append(" - lang: ").append(termItem.getKey()).append("\n");
|
||||
sb.append(" name: ").append(termItem.getValue().getName()).append("\n");
|
||||
sb.append(" url: ").append(termItem.getValue().getUrl()).append("\n");
|
||||
if (policyObject.getTerms() != null) {
|
||||
for (Map.Entry<String, TermObject> termItem : policyObject.getTerms().entrySet()) {
|
||||
sb.append(" - lang: ").append(termItem.getKey()).append("\n");
|
||||
sb.append(" name: ").append(termItem.getValue().getName()).append("\n");
|
||||
sb.append(" url: ").append(termItem.getValue().getUrl()).append("\n");
|
||||
}
|
||||
}
|
||||
LOGGER.info(sb.toString());
|
||||
}
|
||||
|
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;
|
||||
|
||||
public class SQLiteStorageConfig {
|
||||
public class SQLiteStorageConfig implements DatabaseStorageConfig {
|
||||
|
||||
private String database;
|
||||
|
||||
@Override
|
||||
public String getDatabase() {
|
||||
return database;
|
||||
}
|
||||
|
@@ -20,7 +20,7 @@
|
||||
|
||||
package io.kamax.mxisd.config;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -34,6 +34,7 @@ public class ServerConfig {
|
||||
private String name;
|
||||
private int port = 8090;
|
||||
private String publicUrl;
|
||||
private String hostname;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
@@ -59,6 +60,14 @@ public class ServerConfig {
|
||||
this.publicUrl = publicUrl;
|
||||
}
|
||||
|
||||
public String getHostname() {
|
||||
return hostname;
|
||||
}
|
||||
|
||||
public void setHostname(String hostname) {
|
||||
this.hostname = hostname;
|
||||
}
|
||||
|
||||
public void build() {
|
||||
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>"));
|
||||
}
|
||||
|
||||
if (StringUtils.isBlank(getHostname())) {
|
||||
setHostname("0.0.0.0");
|
||||
}
|
||||
|
||||
log.info("Name: {}", getName());
|
||||
log.info("Port: {}", getPort());
|
||||
log.info("Public URL: {}", getPublicUrl());
|
||||
log.info("Hostname: {}", getHostname());
|
||||
}
|
||||
}
|
||||
|
@@ -47,6 +47,8 @@ public class SessionConfig {
|
||||
public static class PolicyUnbind {
|
||||
|
||||
private boolean enabled = true;
|
||||
|
||||
private boolean notifications = true;
|
||||
|
||||
public boolean getEnabled() {
|
||||
return enabled;
|
||||
@@ -55,11 +57,20 @@ public class SessionConfig {
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public boolean shouldNotify() {
|
||||
return notifications;
|
||||
}
|
||||
|
||||
public void setNotifications(boolean notifications) {
|
||||
this.notifications = notifications;
|
||||
}
|
||||
}
|
||||
|
||||
public Policy() {
|
||||
validation.enabled = true;
|
||||
unbind.enabled = true;
|
||||
unbind.notifications = true;
|
||||
}
|
||||
|
||||
private PolicyTemplate validation = new PolicyTemplate();
|
||||
|
@@ -21,14 +21,21 @@
|
||||
package io.kamax.mxisd.config;
|
||||
|
||||
import io.kamax.mxisd.exception.ConfigurationException;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
public class StorageConfig {
|
||||
|
||||
public enum BackendEnum {
|
||||
sqlite,
|
||||
|
||||
postgresql
|
||||
}
|
||||
|
||||
public static class Provider {
|
||||
|
||||
private SQLiteStorageConfig sqlite = new SQLiteStorageConfig();
|
||||
|
||||
private PostgresqlStorageConfig postgresql = new PostgresqlStorageConfig();
|
||||
|
||||
public SQLiteStorageConfig getSqlite() {
|
||||
return sqlite;
|
||||
}
|
||||
@@ -37,16 +44,23 @@ public class StorageConfig {
|
||||
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();
|
||||
|
||||
public String getBackend() {
|
||||
public BackendEnum getBackend() {
|
||||
return backend;
|
||||
}
|
||||
|
||||
public void setBackend(String backend) {
|
||||
public void setBackend(BackendEnum backend) {
|
||||
this.backend = backend;
|
||||
}
|
||||
|
||||
@@ -59,7 +73,7 @@ public class StorageConfig {
|
||||
}
|
||||
|
||||
public void build() {
|
||||
if (StringUtils.isBlank(getBackend())) {
|
||||
if (getBackend() == null) {
|
||||
throw new ConfigurationException("storage.backend");
|
||||
}
|
||||
}
|
||||
|
@@ -21,7 +21,7 @@
|
||||
package io.kamax.mxisd.config;
|
||||
|
||||
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.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;
|
||||
|
||||
import io.kamax.matrix.json.GsonUtil;
|
||||
import io.kamax.mxisd.exception.ConfigurationException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
import org.yaml.snakeyaml.constructor.Constructor;
|
||||
import org.yaml.snakeyaml.introspector.BeanAccess;
|
||||
import org.yaml.snakeyaml.parser.ParserException;
|
||||
import org.yaml.snakeyaml.representer.Representer;
|
||||
import org.yaml.snakeyaml.LoaderOptions;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.File;
|
||||
import java.util.Optional;
|
||||
|
||||
public class YamlConfigLoader {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(YamlConfigLoader.class);
|
||||
|
||||
public static MxisdConfig loadFromFile(String path) throws IOException {
|
||||
File f = new File(path).getAbsoluteFile();
|
||||
log.info("Reading config from {}", f.toString());
|
||||
Representer rep = new Representer();
|
||||
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);
|
||||
public static MxisdConfig loadFromFile(String path) throws IOException {
|
||||
File file = new File(path); // Define the file from the path
|
||||
Constructor constructor = new Constructor(MxisdConfig.class); // Ensure correct import
|
||||
Yaml yaml = new Yaml(constructor); // No change needed here, this is correct
|
||||
|
||||
// SnakeYaml set objects to null when there is no value set in the config, even a full sub-tree.
|
||||
// This is problematic for default config values and objects, to avoid NPEs.
|
||||
// Therefore, we'll use Gson to re-parse the data in a way that avoids us checking the whole config for nulls.
|
||||
MxisdConfig cfg = GsonUtil.get().fromJson(GsonUtil.get().toJson(raw), MxisdConfig.class);
|
||||
|
||||
log.info("Loaded config from {}", path);
|
||||
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");
|
||||
}
|
||||
// Load from YAML
|
||||
try (FileInputStream inputStream = new FileInputStream(file)) {
|
||||
return yaml.loadAs(inputStream, MxisdConfig.class);
|
||||
} catch (IOException e) {
|
||||
// Handle exceptions
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public static Optional<MxisdConfig> tryLoadFromFile(String path) {
|
||||
log.debug("Attempting to read config from {}", path);
|
||||
try {
|
||||
return Optional.of(loadFromFile(path));
|
||||
} catch (FileNotFoundException e) {
|
||||
log.info("No config file at {}", path);
|
||||
return Optional.empty();
|
||||
} 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.mxisd.backend.ldap.LdapBackend;
|
||||
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.LoggerFactory;
|
||||
|
||||
@@ -233,6 +233,7 @@ public abstract class LdapConfig {
|
||||
private String filter;
|
||||
private String token = "%3pid";
|
||||
private Map<String, String> medium = new HashMap<>();
|
||||
private boolean lookup = false;
|
||||
|
||||
public String getFilter() {
|
||||
return filter;
|
||||
@@ -262,6 +263,13 @@ public abstract class LdapConfig {
|
||||
this.medium = medium;
|
||||
}
|
||||
|
||||
public boolean isLookup() {
|
||||
return lookup;
|
||||
}
|
||||
|
||||
public void setLookup(boolean lookup) {
|
||||
this.lookup = lookup;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Profile {
|
||||
@@ -283,6 +291,9 @@ public abstract class LdapConfig {
|
||||
private boolean enabled;
|
||||
private String filter;
|
||||
|
||||
private boolean activeDirectory;
|
||||
private String defaultDomain;
|
||||
|
||||
private Connection connection = new Connection();
|
||||
private Attribute attribute = new Attribute();
|
||||
private Auth auth = new Auth();
|
||||
@@ -308,6 +319,22 @@ public abstract class LdapConfig {
|
||||
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() {
|
||||
return connection;
|
||||
}
|
||||
@@ -399,6 +426,15 @@ public abstract class LdapConfig {
|
||||
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
|
||||
attribute.getThreepid().forEach((k, v) -> {
|
||||
if (StringUtils.isBlank(identity.getMedium().get(k))) {
|
||||
|
@@ -27,6 +27,7 @@ public class MemoryStoreConfig {
|
||||
|
||||
private boolean enabled;
|
||||
private List<MemoryIdentityConfig> identities = new ArrayList<>();
|
||||
private boolean hashEnabled = false;
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
@@ -44,6 +45,14 @@ public class MemoryStoreConfig {
|
||||
this.identities = identities;
|
||||
}
|
||||
|
||||
public boolean isHashEnabled() {
|
||||
return hashEnabled;
|
||||
}
|
||||
|
||||
public void setHashEnabled(boolean hashEnabled) {
|
||||
this.hashEnabled = hashEnabled;
|
||||
}
|
||||
|
||||
public void build() {
|
||||
// no-op
|
||||
}
|
||||
|
@@ -45,4 +45,21 @@ public class MemoryThreePid implements _ThreePid {
|
||||
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;
|
||||
|
||||
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.LoggerFactory;
|
||||
|
||||
@@ -33,8 +33,8 @@ public class RestBackendConfig {
|
||||
|
||||
public static class IdentityEndpoints {
|
||||
|
||||
private String single = "/_ma1sd/backend/api/v1/identity/single";
|
||||
private String bulk = "/_ma1sd/backend/api/v1/identity/bulk";
|
||||
private String single = "/_mxids/backend/api/v1/identity/single";
|
||||
private String bulk = "/_mxids/backend/api/v1/identity/bulk";
|
||||
|
||||
public String getSingle() {
|
||||
return single;
|
||||
@@ -56,9 +56,9 @@ public class RestBackendConfig {
|
||||
|
||||
public static class ProfileEndpoints {
|
||||
|
||||
private String displayName = "/_ma1sd/backend/api/v1/profile/displayName";
|
||||
private String threepids = "/_ma1sd/backend/api/v1/profile/threepids";
|
||||
private String roles = "/_ma1sd/backend/api/v1/profile/roles";
|
||||
private String displayName = "/_mxids/backend/api/v1/profile/displayName";
|
||||
private String threepids = "/_mxids/backend/api/v1/profile/threepids";
|
||||
private String roles = "/_mxids/backend/api/v1/profile/roles";
|
||||
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
@@ -88,8 +88,8 @@ public class RestBackendConfig {
|
||||
|
||||
public static class Endpoints {
|
||||
|
||||
private String auth = "/_ma1sd/backend/api/v1/auth/login";
|
||||
private String directory = "/_ma1sd/backend/api/v1/directory/user/search";
|
||||
private String auth = "/_mxids/backend/api/v1/auth/login";
|
||||
private String directory = "/_mxids/backend/api/v1/directory/user/search";
|
||||
private IdentityEndpoints identity = new IdentityEndpoints();
|
||||
private ProfileEndpoints profile = new ProfileEndpoints();
|
||||
|
||||
|
@@ -125,7 +125,7 @@ public abstract class SqlConfig {
|
||||
}
|
||||
|
||||
public static class Lookup {
|
||||
private String query;
|
||||
private String query = "SELECT user_id AS mxid, medium, address from user_threepid_id_server";
|
||||
|
||||
public String getQuery() {
|
||||
return query;
|
||||
@@ -140,7 +140,7 @@ public abstract class SqlConfig {
|
||||
|
||||
private Boolean enabled;
|
||||
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<>();
|
||||
|
||||
public Boolean isEnabled() {
|
||||
|
@@ -23,10 +23,24 @@ package io.kamax.mxisd.config.sql.synapse;
|
||||
import io.kamax.mxisd.UserIdType;
|
||||
import io.kamax.mxisd.backend.sql.synapse.SynapseQueries;
|
||||
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 {
|
||||
|
||||
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
|
||||
protected String getProviderName() {
|
||||
return "Synapse SQL";
|
||||
@@ -42,7 +56,7 @@ public class SynapseSqlProviderConfig extends SqlConfig {
|
||||
|
||||
if (getIdentity().isEnabled() && StringUtils.isBlank(getIdentity().getType())) {
|
||||
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()) {
|
||||
@@ -65,4 +79,12 @@ public class SynapseSqlProviderConfig extends SqlConfig {
|
||||
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;
|
||||
|
||||
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.LoggerFactory;
|
||||
|
||||
|
@@ -20,7 +20,7 @@
|
||||
|
||||
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.LoggerFactory;
|
||||
|
||||
|
@@ -20,7 +20,7 @@
|
||||
|
||||
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.Map;
|
||||
|
@@ -21,7 +21,7 @@
|
||||
package io.kamax.mxisd.config.wordpress;
|
||||
|
||||
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.Map;
|
||||
|
@@ -31,7 +31,7 @@ import io.kamax.mxisd.http.io.UserDirectorySearchRequest;
|
||||
import io.kamax.mxisd.http.io.UserDirectorySearchResult;
|
||||
import io.kamax.mxisd.util.RestClientUtils;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.client.utils.URIBuilder;
|
||||
|
@@ -23,5 +23,6 @@ package io.kamax.mxisd.exception;
|
||||
public class InvalidParamException extends RuntimeException {
|
||||
|
||||
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 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;
|
||||
|
||||
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.NoOpRotationStrategy;
|
||||
import io.kamax.mxisd.hash.rotation.RotationPerRequests;
|
||||
@@ -21,7 +24,7 @@ public class HashManager {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(HashManager.class);
|
||||
|
||||
private HashEngine hashEngine;
|
||||
private Engine engine;
|
||||
private HashRotationStrategy rotationStrategy;
|
||||
private HashStorage hashStorage;
|
||||
private HashingConfig config;
|
||||
@@ -32,7 +35,7 @@ public class HashManager {
|
||||
this.config = config;
|
||||
this.storage = storage;
|
||||
initStorage();
|
||||
hashEngine = new HashEngine(providers, getHashStorage(), config);
|
||||
engine = config.isEnabled() ? new HashEngine(providers, getHashStorage(), config) : new NoneEngine();
|
||||
initRotationStrategy();
|
||||
configured.set(true);
|
||||
}
|
||||
@@ -40,10 +43,10 @@ public class HashManager {
|
||||
private void initStorage() {
|
||||
if (config.isEnabled()) {
|
||||
switch (config.getHashStorageType()) {
|
||||
case IN_MEMORY:
|
||||
case in_memory:
|
||||
this.hashStorage = new InMemoryHashStorage();
|
||||
break;
|
||||
case SQL:
|
||||
case sql:
|
||||
this.hashStorage = new SqlHashStorage(storage);
|
||||
break;
|
||||
default:
|
||||
@@ -57,11 +60,11 @@ public class HashManager {
|
||||
private void initRotationStrategy() {
|
||||
if (config.isEnabled()) {
|
||||
switch (config.getRotationPolicy()) {
|
||||
case PER_REQUESTS:
|
||||
this.rotationStrategy = new RotationPerRequests();
|
||||
case per_requests:
|
||||
this.rotationStrategy = new RotationPerRequests(config.getRequests());
|
||||
break;
|
||||
case PER_SECONDS:
|
||||
this.rotationStrategy = new TimeBasedRotation(config.getDelay());
|
||||
case per_seconds:
|
||||
this.rotationStrategy = new TimeBasedRotation(config.getDelayInSeconds());
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown rotation type: " + config.getHashStorageType());
|
||||
@@ -73,8 +76,8 @@ public class HashManager {
|
||||
this.rotationStrategy.register(getHashEngine());
|
||||
}
|
||||
|
||||
public HashEngine getHashEngine() {
|
||||
return hashEngine;
|
||||
public Engine getHashEngine() {
|
||||
return engine;
|
||||
}
|
||||
|
||||
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,20 +1,25 @@
|
||||
package io.kamax.mxisd.hash;
|
||||
package io.kamax.mxisd.hash.engine;
|
||||
|
||||
import io.kamax.matrix.codec.MxSha256;
|
||||
import io.kamax.mxisd.config.HashingConfig;
|
||||
import io.kamax.mxisd.hash.storage.HashStorage;
|
||||
import io.kamax.mxisd.lookup.ThreePidMapping;
|
||||
import io.kamax.mxisd.lookup.provider.IThreePidProvider;
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Base64;
|
||||
import java.util.List;
|
||||
|
||||
public class HashEngine {
|
||||
public class HashEngine implements Engine {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(HashEngine.class);
|
||||
|
||||
private final List<? extends IThreePidProvider> providers;
|
||||
private final HashStorage hashStorage;
|
||||
private final MxSha256 sha256 = new MxSha256();
|
||||
private final HashingConfig config;
|
||||
private final Base64.Encoder base64 = Base64.getUrlEncoder().withoutPadding();
|
||||
private String pepper;
|
||||
|
||||
public HashEngine(List<? extends IThreePidProvider> providers, HashStorage hashStorage, HashingConfig config) {
|
||||
@@ -23,18 +28,28 @@ public class HashEngine {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateHashes() {
|
||||
LOGGER.info("Start update hashes.");
|
||||
synchronized (hashStorage) {
|
||||
this.pepper = newPepper();
|
||||
hashStorage.clear();
|
||||
for (IThreePidProvider provider : providers) {
|
||||
for (ThreePidMapping pidMapping : provider.populateHashes()) {
|
||||
hashStorage.add(pidMapping, hash(pidMapping));
|
||||
try {
|
||||
LOGGER.info("Populate hashes from the handler: {}", provider.getClass().getCanonicalName());
|
||||
for (ThreePidMapping pidMapping : provider.populateHashes()) {
|
||||
LOGGER.debug("Found 3PID: {}", pidMapping);
|
||||
hashStorage.add(pidMapping, hash(pidMapping));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("Unable to update hashes of the provider: " + provider.toString(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
LOGGER.info("Finish update hashes.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPepper() {
|
||||
synchronized (hashStorage) {
|
||||
return pepper;
|
||||
@@ -42,10 +57,10 @@ public class HashEngine {
|
||||
}
|
||||
|
||||
protected String hash(ThreePidMapping pidMapping) {
|
||||
return sha256.hash(pidMapping.getMedium() + " " + pidMapping.getValue() + " " + getPepper());
|
||||
return base64.encodeToString(DigestUtils.sha256(pidMapping.getValue() + " " + pidMapping.getMedium() + " " + getPepper()));
|
||||
}
|
||||
|
||||
protected String newPepper() {
|
||||
return RandomStringUtils.random(config.getPepperLength());
|
||||
return RandomStringUtils.random(config.getPepperLength(), true, true);
|
||||
}
|
||||
}
|
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;
|
||||
|
||||
import io.kamax.mxisd.hash.HashEngine;
|
||||
import io.kamax.mxisd.hash.engine.Engine;
|
||||
|
||||
public interface HashRotationStrategy {
|
||||
|
||||
void register(HashEngine hashEngine);
|
||||
void register(Engine engine);
|
||||
|
||||
HashEngine getHashEngine();
|
||||
Engine getHashEngine();
|
||||
|
||||
void newRequest();
|
||||
|
||||
|
@@ -1,19 +1,19 @@
|
||||
package io.kamax.mxisd.hash.rotation;
|
||||
|
||||
import io.kamax.mxisd.hash.HashEngine;
|
||||
import io.kamax.mxisd.hash.engine.Engine;
|
||||
|
||||
public class NoOpRotationStrategy implements HashRotationStrategy {
|
||||
|
||||
private HashEngine hashEngine;
|
||||
private Engine engine;
|
||||
|
||||
@Override
|
||||
public void register(HashEngine hashEngine) {
|
||||
this.hashEngine = hashEngine;
|
||||
public void register(Engine engine) {
|
||||
this.engine = engine;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HashEngine getHashEngine() {
|
||||
return hashEngine;
|
||||
public Engine getHashEngine() {
|
||||
return engine;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -1,28 +1,34 @@
|
||||
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;
|
||||
|
||||
public class RotationPerRequests implements HashRotationStrategy {
|
||||
|
||||
private HashEngine hashEngine;
|
||||
private Engine engine;
|
||||
private final AtomicInteger counter = new AtomicInteger(0);
|
||||
private final int barrier;
|
||||
|
||||
@Override
|
||||
public void register(HashEngine hashEngine) {
|
||||
this.hashEngine = hashEngine;
|
||||
public RotationPerRequests(int barrier) {
|
||||
this.barrier = barrier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HashEngine getHashEngine() {
|
||||
return hashEngine;
|
||||
public void register(Engine engine) {
|
||||
this.engine = engine;
|
||||
trigger();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Engine getHashEngine() {
|
||||
return engine;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void newRequest() {
|
||||
int newValue = counter.incrementAndGet();
|
||||
if (newValue >= 10) {
|
||||
if (newValue >= barrier) {
|
||||
counter.set(0);
|
||||
trigger();
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
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.ScheduledExecutorService;
|
||||
@@ -9,7 +9,7 @@ import java.util.concurrent.TimeUnit;
|
||||
public class TimeBasedRotation implements HashRotationStrategy {
|
||||
|
||||
private final long delay;
|
||||
private HashEngine hashEngine;
|
||||
private Engine engine;
|
||||
private final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
|
||||
|
||||
public TimeBasedRotation(long delay) {
|
||||
@@ -17,15 +17,15 @@ public class TimeBasedRotation implements HashRotationStrategy {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(HashEngine hashEngine) {
|
||||
this.hashEngine = hashEngine;
|
||||
public void register(Engine engine) {
|
||||
this.engine = engine;
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(executorService::shutdown));
|
||||
executorService.scheduleWithFixedDelay(this::trigger, 0, delay, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HashEngine getHashEngine() {
|
||||
return hashEngine;
|
||||
public Engine getHashEngine() {
|
||||
return engine;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -12,6 +12,7 @@ public class SqlHashStorage implements HashStorage {
|
||||
|
||||
public SqlHashStorage(IStorage storage) {
|
||||
this.storage = storage;
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(storage::clearHashes));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user