Compare commits
24 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
a964b073bf | ||
|
f85345bc97 | ||
|
29603682e5 | ||
|
d54f1dcb88 | ||
|
92f10347d1 | ||
|
0298f66212 | ||
|
0ddd086bda | ||
|
544f8e59f0 | ||
|
917f87bf8c | ||
|
774795c203 | ||
|
27b2976e42 | ||
|
f16f184253 | ||
|
cd890d114a | ||
|
321ba1e325 | ||
|
c3ce0a17f6 | ||
|
0fcc0d9bb2 | ||
|
ce7f900543 | ||
|
c7c009f9af | ||
|
3b01663245 | ||
|
9cc601d582 | ||
|
e6272b1827 | ||
|
8243354f39 | ||
|
25968e0737 | ||
|
44a80461a0 |
14
README.md
14
README.md
@@ -14,7 +14,7 @@ mxisd - Federated Matrix Identity Server
|
|||||||
|
|
||||||
# Overview
|
# Overview
|
||||||
mxisd is a Federated Matrix Identity server for self-hosted Matrix infrastructures with [enhanced features](#features).
|
mxisd is a Federated Matrix Identity server for self-hosted Matrix infrastructures with [enhanced features](#features).
|
||||||
As an enhanced Identity service, it implements the [Identity service API](https://matrix.org/docs/spec/identity_service/r0.1.0.html)
|
As an enhanced Identity service, it implements the [Identity service API](https://matrix.org/docs/spec/identity_service/r0.2.0.html)
|
||||||
and several [extra features](#features) that greatly enhance user experience within Matrix.
|
and several [extra features](#features) that greatly enhance user experience within Matrix.
|
||||||
It is the one stop shop for anything regarding Authentication, Directory and Identity management in Matrix built in a
|
It is the one stop shop for anything regarding Authentication, Directory and Identity management in Matrix built in a
|
||||||
single coherent product.
|
single coherent product.
|
||||||
@@ -34,15 +34,15 @@ users. 3PIDs can be anything that uniquely and globally identify a user, like:
|
|||||||
If you are unfamiliar with the Identity vocabulary and concepts in Matrix, **please read this [introduction](docs/concepts.md)**.
|
If you are unfamiliar with the Identity vocabulary and concepts in Matrix, **please read this [introduction](docs/concepts.md)**.
|
||||||
|
|
||||||
# Features
|
# Features
|
||||||
[Identity](docs/features/identity.md): As a [regular Matrix Identity service](https://matrix.org/docs/spec/identity_service/r0.1.0.html#general-principles):
|
[Identity](docs/features/identity.md): As a [regular Matrix Identity service](https://matrix.org/docs/spec/identity_service/r0.2.0.html#general-principles):
|
||||||
- Search for people by 3PID using its own Identity stores
|
- Search for people by 3PID using its own Identity stores
|
||||||
([Spec](https://matrix.org/docs/spec/identity_service/r0.1.0.html#association-lookup))
|
([Spec](https://matrix.org/docs/spec/identity_service/r0.2.0.html#association-lookup))
|
||||||
- Invite people to rooms by 3PID using its own Identity stores, with notifications to the invitee (Email, SMS, etc.)
|
- Invite people to rooms by 3PID using its own Identity stores, with notifications to the invitee (Email, SMS, etc.)
|
||||||
([Spec](https://matrix.org/docs/spec/identity_service/r0.1.0.html#post-matrix-identity-api-v1-store-invite))
|
([Spec](https://matrix.org/docs/spec/identity_service/r0.2.0.html#invitation-storage))
|
||||||
- Allow users to add 3PIDs to their settings/profile
|
- Allow users to add/remove 3PIDs to their settings/profile via 3PID sessions
|
||||||
([Spec](https://matrix.org/docs/spec/identity_service/r0.1.0.html#establishing-associations))
|
([Spec](https://matrix.org/docs/spec/identity_service/r0.2.0.html#establishing-associations))
|
||||||
- Register accounts on your Homeserver with 3PIDs
|
- Register accounts on your Homeserver with 3PIDs
|
||||||
([Spec](https://matrix.org/docs/spec/identity_service/r0.1.0.html#establishing-associations))
|
([Spec](https://matrix.org/docs/spec/identity_service/r0.2.0.html#establishing-associations))
|
||||||
|
|
||||||
As an enhanced Identity service:
|
As an enhanced Identity service:
|
||||||
- [Federation](docs/features/federation.md): Use a recursive lookup mechanism when searching and inviting people by 3PID,
|
- [Federation](docs/features/federation.md): Use a recursive lookup mechanism when searching and inviting people by 3PID,
|
||||||
|
@@ -48,6 +48,8 @@ def dockerImageTag = "${dockerImageName}:${mxisdVersion()}"
|
|||||||
|
|
||||||
group = 'io.kamax'
|
group = 'io.kamax'
|
||||||
mainClassName = 'io.kamax.mxisd.MxisdStandaloneExec'
|
mainClassName = 'io.kamax.mxisd.MxisdStandaloneExec'
|
||||||
|
sourceCompatibility = '1.8'
|
||||||
|
targetCompatibility = '1.8'
|
||||||
|
|
||||||
String mxisdVersion() {
|
String mxisdVersion() {
|
||||||
def versionPattern = Pattern.compile("v(\\d+\\.)?(\\d+\\.)?(\\d+)(-.*)?")
|
def versionPattern = Pattern.compile("v(\\d+\\.)?(\\d+\\.)?(\\d+)(-.*)?")
|
||||||
@@ -227,6 +229,12 @@ task debBuild(dependsOn: shadowJar) {
|
|||||||
value: debDataPath
|
value: debDataPath
|
||||||
)
|
)
|
||||||
|
|
||||||
|
ant.replace(
|
||||||
|
file: "${debBuildDebianPath}/postinst",
|
||||||
|
token: '%DEB_CONF_FILE%',
|
||||||
|
value: "${debConfPath}/mxisd.yaml"
|
||||||
|
)
|
||||||
|
|
||||||
ant.chmod(
|
ant.chmod(
|
||||||
file: "${debBuildDebianPath}/postinst",
|
file: "${debBuildDebianPath}/postinst",
|
||||||
perm: 'a+x'
|
perm: 'a+x'
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
# Application Service
|
# Application Service
|
||||||
**WARNING:** These features are currently highly experimental. They can be removed or modified without notice.
|
**WARNING:** These features are currently highly experimental. They can be removed or modified without notice.
|
||||||
All the features requires a Homeserver capable of connecting [Application Services](https://matrix.org/docs/spec/application_service/r0.1.0.html).
|
All the features requires a Homeserver capable of connecting [Application Services](https://matrix.org/docs/spec/application_service/r0.1.1.html).
|
||||||
|
|
||||||
The following capabilities are provided in this feature:
|
The following capabilities are provided in this feature:
|
||||||
- [Admin commands](#admin-commands)
|
- [Admin commands](#admin-commands)
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
# Identity
|
# Identity
|
||||||
Implementation of the [Identity Service API r0.1.0](https://matrix.org/docs/spec/identity_service/r0.1.0.html).
|
Implementation of the [Identity Service API r0.2.0](https://matrix.org/docs/spec/identity_service/r0.2.0.html).
|
||||||
|
|
||||||
- [Lookups](#lookups)
|
- [Lookups](#lookups)
|
||||||
- [Invitations](#invitations)
|
- [Invitations](#invitations)
|
||||||
|
@@ -3,7 +3,7 @@
|
|||||||
- [Integration](#integration)
|
- [Integration](#integration)
|
||||||
- [Reverse Proxy](#reverse-proxy)
|
- [Reverse Proxy](#reverse-proxy)
|
||||||
- [nginx](#nginx)
|
- [nginx](#nginx)
|
||||||
- [Apache](#apache)
|
- [Apache2](#apache2)
|
||||||
- [Homeserver](#homeserver)
|
- [Homeserver](#homeserver)
|
||||||
- [synapse](#synapse)
|
- [synapse](#synapse)
|
||||||
- [Configuration](#configuration)
|
- [Configuration](#configuration)
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
Registration is an enhanced feature of mxisd to control registrations involving 3PIDs on a Homeserver based on policies:
|
Registration is an enhanced feature of mxisd to control registrations involving 3PIDs on a Homeserver based on policies:
|
||||||
- Match pending 3PID invites on the server
|
- Match pending 3PID invites on the server
|
||||||
- Match 3PID pattern, like a specific set of domains for emails
|
- Match 3PID pattern, like a specific set of domains for emails
|
||||||
- In futher releases, use 3PIDs found in Identity stores
|
- In further releases, use 3PIDs found in Identity stores
|
||||||
|
|
||||||
It aims to help open or invite-only registration servers control what is possible to do and ensure only approved people
|
It aims to help open or invite-only registration servers control what is possible to do and ensure only approved people
|
||||||
can register on a given server in a implementation-agnostic manner.
|
can register on a given server in a implementation-agnostic manner.
|
||||||
@@ -36,14 +36,14 @@ Later version(s) of this feature may directly control registration itself to cre
|
|||||||
### Reverse Proxy
|
### Reverse Proxy
|
||||||
#### nginx
|
#### nginx
|
||||||
```nginx
|
```nginx
|
||||||
location ^/_matrix/client/r0/register/[^/]/?$ {
|
location ~* ^/_matrix/client/r0/register/[^/]+/requestToken$ {
|
||||||
proxy_pass http://127.0.0.1:8090;
|
proxy_pass http://localhost:8090;
|
||||||
proxy_set_header Host $host;
|
proxy_set_header Host $host;
|
||||||
proxy_set_header X-Forwarded-For $remote_addr;
|
proxy_set_header X-Forwarded-For $remote_addr;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
#### apache
|
#### Apache2
|
||||||
> TBC
|
> TBC
|
||||||
|
|
||||||
### Homeserver
|
### Homeserver
|
||||||
@@ -55,8 +55,8 @@ registrations_require_3pid:
|
|||||||
```
|
```
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
See the [Configuration](../configuration.md) introduction doc on how to read the configuration keys.
|
See the [Configuration](../configure.md) introduction doc on how to read the configuration keys.
|
||||||
An example of working configuration is avaiable at the end of this section.
|
An example of working configuration is available at the end of this section.
|
||||||
### Enable/Disable
|
### Enable/Disable
|
||||||
`register.allowed`, taking a boolean, can be used to enable/disable registration if the attempt is not 3PID-based.
|
`register.allowed`, taking a boolean, can be used to enable/disable registration if the attempt is not 3PID-based.
|
||||||
`false` is the default value to prevent open registration, as you must allow it on the homeserver side.
|
`false` is the default value to prevent open registration, as you must allow it on the homeserver side.
|
||||||
@@ -72,7 +72,7 @@ At this time, only `email` is supported with 3PID specific configuration with th
|
|||||||
**Base key**: `register.threepid.email`
|
**Base key**: `register.threepid.email`
|
||||||
|
|
||||||
##### Domain whitelist/blacklist
|
##### Domain whitelist/blacklist
|
||||||
If you would like to control which domains are allowed to be used when registrating with an email, the following sub-keys
|
If you would like to control which domains are allowed to be used when registering with an email, the following sub-keys
|
||||||
are available:
|
are available:
|
||||||
- `domain.whitelist`
|
- `domain.whitelist`
|
||||||
- `domain.blacklist`
|
- `domain.blacklist`
|
||||||
@@ -82,7 +82,7 @@ The value format is an hybrid between glob patterns and postfix configuration fi
|
|||||||
- `.<domain>` will only match sub-domain(s)
|
- `.<domain>` will only match sub-domain(s)
|
||||||
- `<domain>` will only match the exact domain
|
- `<domain>` will only match the exact domain
|
||||||
|
|
||||||
The following table illustrates pattern and maching status against example values:
|
The following table illustrates pattern and matching status against example values:
|
||||||
|
|
||||||
| Config value | Matches `example.org` | Matches `sub.example.org` |
|
| Config value | Matches `example.org` | Matches `sub.example.org` |
|
||||||
|--------------- |-----------------------|---------------------------|
|
|--------------- |-----------------------|---------------------------|
|
||||||
|
@@ -20,27 +20,31 @@ All placeholders **MUST** be surrounded with `%` in the template. Per example, t
|
|||||||
The following placeholders are available in every template:
|
The following placeholders are available in every template:
|
||||||
|
|
||||||
| Placeholder | Purpose |
|
| Placeholder | Purpose |
|
||||||
|---------------------|------------------------------------------------------------------------------|
|
|---------------------------------|------------------------------------------------------------------------------|
|
||||||
| `DOMAIN` | Identity server authoritative domain, as configured in `matrix.domain` |
|
| `DOMAIN` | Identity server authoritative domain, as configured in `matrix.domain` |
|
||||||
| `DOMAIN_PRETTY` | Same as `DOMAIN` with the first letter upper case and all other lower case |
|
| `DOMAIN_PRETTY` | Same as `DOMAIN` with the first letter upper case and all other lower case |
|
||||||
| `FROM_EMAIL` | Email address configured in `threepid.medium.<3PID medium>.identity.from` |
|
| `FROM_EMAIL` | Email address configured in `threepid.medium.<3PID medium>.identity.from` |
|
||||||
| `FROM_NAME` | Name configured in `threepid.medium.<3PID medium>.identity.name` |
|
| `FROM_NAME` | Name configured in `threepid.medium.<3PID medium>.identity.name` |
|
||||||
| `RECIPIENT_MEDIUM` | The 3PID medium, like `email` or `msisdn` |
|
| `RECIPIENT_MEDIUM` | The 3PID medium, like `email` or `msisdn` |
|
||||||
|
| `RECIPIENT_MEDIUM_URL_ENCODED` | URL encoded value of `RECIPIENT_MEDIUM` |
|
||||||
| `RECIPIENT_ADDRESS` | The address to which the notification is sent |
|
| `RECIPIENT_ADDRESS` | The address to which the notification is sent |
|
||||||
|
| `RECIPIENT_ADDRESS_URL_ENCODED` | URL encoded value of `RECIPIENT_ADDRESS` |
|
||||||
|
|
||||||
### Room invitation
|
### Room invitation
|
||||||
Specific placeholders:
|
Specific placeholders:
|
||||||
|
|
||||||
| Placeholder | Purpose |
|
| Placeholder | Purpose |
|
||||||
|---------------------|------------------------------------------------------------------------------------------|
|
|------------------------------|-----------------------------------------------------------------------------------|
|
||||||
| `SENDER_ID` | Matrix ID of the user who made the invite |
|
| `SENDER_ID` | Matrix ID of the user who made the invite |
|
||||||
| `SENDER_NAME` | Display name of the user who made the invite, if not available/set, empty |
|
| `SENDER_NAME` | Display name of the user who made the invite, if not available/set, empty |
|
||||||
| `SENDER_NAME_OR_ID` | Display name of the user who made the invite. If not available/set, its Matrix ID |
|
| `SENDER_NAME_OR_ID` | Display name of the user who made the invite. If not available/set, its Matrix ID |
|
||||||
| `INVITE_MEDIUM` | The 3PID medium for the invite. |
|
| `INVITE_MEDIUM` | The 3PID medium for the invite. |
|
||||||
|
| `INVITE_MEDIUM_URL_ENCODED` | URL encoded value of `INVITE_MEDIUM` |
|
||||||
| `INVITE_ADDRESS` | The 3PID address for the invite. |
|
| `INVITE_ADDRESS` | The 3PID address for the invite. |
|
||||||
|
| `INVITE_ADDRESS_URL_ENCODED` | URL encoded value of `INVITE_ADDRESS` |
|
||||||
| `ROOM_ID` | The Matrix ID of the Room in which the invite took place |
|
| `ROOM_ID` | The Matrix ID of the Room in which the invite took place |
|
||||||
| `ROOM_NAME` | The Name of the room in which the invite took place. If not available/set, empty |
|
| `ROOM_NAME` | The Name of the room in which the invite took place. If not available/set, empty |
|
||||||
| `ROOM_NAME_OR_ID` | The Name of the room in which the invite took place. If not available/set, its Matrix ID |
|
| `ROOM_NAME_OR_ID` | The Name of the room in which the invite took place. If not available/set, its ID |
|
||||||
| `REGISTER_URL` | The URL to provide to the user allowing them to register their account, if needed |
|
| `REGISTER_URL` | The URL to provide to the user allowing them to register their account, if needed |
|
||||||
|
|
||||||
### Validation of 3PID Session
|
### Validation of 3PID Session
|
||||||
|
@@ -26,6 +26,14 @@ Two configuration keys are available that accept paths to HTML templates:
|
|||||||
- `success`
|
- `success`
|
||||||
- `failure`
|
- `failure`
|
||||||
|
|
||||||
|
### Serving static assets
|
||||||
|
mxisd will not serve any static asset (images, JS, CSS, etc.). If such are needed, you will need to serve them using the
|
||||||
|
reverse proxy sitting in front of mxisd using a path outside of the `/_matrix/identity/` namespace. We advise using
|
||||||
|
the base path `/static/` for such use cases, allowing to remain under the same hostname/origin.
|
||||||
|
|
||||||
|
You can also serve such assets using absolute URL, possibly under other domains, but be aware of Cross-Origin restrictions
|
||||||
|
in browsers which are out of scope of mxisd.
|
||||||
|
|
||||||
## Placeholders
|
## Placeholders
|
||||||
### Success
|
### Success
|
||||||
No object/placeholder are currently available.
|
No object/placeholder are currently available.
|
||||||
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
3
gradle/wrapper/gradle-wrapper.properties
vendored
3
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,5 @@
|
|||||||
#Fri Aug 11 17:19:02 CEST 2017
|
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-5.3.1-bin.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.0.2-bin.zip
|
|
||||||
|
18
gradlew
vendored
18
gradlew
vendored
@@ -1,5 +1,21 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright 2015 the original author or authors.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
##
|
##
|
||||||
## Gradle start up script for UN*X
|
## Gradle start up script for UN*X
|
||||||
@@ -28,7 +44,7 @@ APP_NAME="Gradle"
|
|||||||
APP_BASE_NAME=`basename "$0"`
|
APP_BASE_NAME=`basename "$0"`
|
||||||
|
|
||||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
DEFAULT_JVM_OPTS=""
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
MAX_FD="maximum"
|
MAX_FD="maximum"
|
||||||
|
18
gradlew.bat
vendored
18
gradlew.bat
vendored
@@ -1,3 +1,19 @@
|
|||||||
|
@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
|
@if "%DEBUG%" == "" @echo off
|
||||||
@rem ##########################################################################
|
@rem ##########################################################################
|
||||||
@rem
|
@rem
|
||||||
@@ -14,7 +30,7 @@ set APP_BASE_NAME=%~n0
|
|||||||
set APP_HOME=%DIRNAME%
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
set DEFAULT_JVM_OPTS=
|
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||||
|
|
||||||
@rem Find java.exe
|
@rem Find java.exe
|
||||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
@@ -11,3 +11,9 @@ ln -sfT /usr/lib/mxisd/mxisd /usr/bin/mxisd
|
|||||||
|
|
||||||
# Enable systemd service
|
# Enable systemd service
|
||||||
systemctl enable mxisd.service
|
systemctl enable mxisd.service
|
||||||
|
|
||||||
|
# If we already have a config file setup, we attempt to run mxisd automatically
|
||||||
|
# Specifically targeted at upgrades where the service needs to be restarted
|
||||||
|
if [ -f "%DEB_CONF_FILE%" ]; then
|
||||||
|
systemctl restart mxisd.service
|
||||||
|
fi
|
||||||
|
@@ -105,7 +105,7 @@ public class HttpMxisd {
|
|||||||
.get(SessionValidateHandler.Path, SaneHandler.around(new SessionValidationGetHandler(m.getSession(), m.getConfig())))
|
.get(SessionValidateHandler.Path, SaneHandler.around(new SessionValidationGetHandler(m.getSession(), m.getConfig())))
|
||||||
.post(SessionValidateHandler.Path, SaneHandler.around(new SessionValidationPostHandler(m.getSession())))
|
.post(SessionValidateHandler.Path, SaneHandler.around(new SessionValidationPostHandler(m.getSession())))
|
||||||
.get(SessionTpidGetValidatedHandler.Path, SaneHandler.around(new SessionTpidGetValidatedHandler(m.getSession())))
|
.get(SessionTpidGetValidatedHandler.Path, SaneHandler.around(new SessionTpidGetValidatedHandler(m.getSession())))
|
||||||
.post(SessionTpidBindHandler.Path, SaneHandler.around(new SessionTpidBindHandler(m.getSession(), m.getInvite())))
|
.post(SessionTpidBindHandler.Path, SaneHandler.around(new SessionTpidBindHandler(m.getSession(), m.getInvite(), m.getSign())))
|
||||||
.post(SessionTpidUnbindHandler.Path, SaneHandler.around(new SessionTpidUnbindHandler(m.getSession())))
|
.post(SessionTpidUnbindHandler.Path, SaneHandler.around(new SessionTpidUnbindHandler(m.getSession())))
|
||||||
.post(SignEd25519Handler.Path, SaneHandler.around(new SignEd25519Handler(m.getConfig(), m.getInvite(), m.getSign())))
|
.post(SignEd25519Handler.Path, SaneHandler.around(new SignEd25519Handler(m.getConfig(), m.getInvite(), m.getSign())))
|
||||||
|
|
||||||
|
@@ -107,7 +107,7 @@ public class Mxisd {
|
|||||||
|
|
||||||
store = new OrmLiteSqlStorage(cfg);
|
store = new OrmLiteSqlStorage(cfg);
|
||||||
keyMgr = CryptoFactory.getKeyManager(cfg.getKey());
|
keyMgr = CryptoFactory.getKeyManager(cfg.getKey());
|
||||||
signMgr = CryptoFactory.getSignatureManager(keyMgr);
|
signMgr = CryptoFactory.getSignatureManager(cfg, keyMgr);
|
||||||
clientDns = new ClientDnsOverwrite(cfg.getDns().getOverwrite());
|
clientDns = new ClientDnsOverwrite(cfg.getDns().getOverwrite());
|
||||||
|
|
||||||
synapse = new Synapse(cfg.getSynapseSql());
|
synapse = new Synapse(cfg.getSynapseSql());
|
||||||
@@ -118,7 +118,7 @@ public class Mxisd {
|
|||||||
idStrategy = new RecursivePriorityLookupStrategy(cfg.getLookup(), ThreePidProviders.get(), bridgeFetcher);
|
idStrategy = new RecursivePriorityLookupStrategy(cfg.getLookup(), ThreePidProviders.get(), bridgeFetcher);
|
||||||
pMgr = new ProfileManager(ProfileProviders.get(), clientDns, httpClient);
|
pMgr = new ProfileManager(ProfileProviders.get(), clientDns, httpClient);
|
||||||
notifMgr = new NotificationManager(cfg.getNotification(), NotificationHandlers.get());
|
notifMgr = new NotificationManager(cfg.getNotification(), NotificationHandlers.get());
|
||||||
sessMgr = new SessionManager(cfg.getSession(), cfg.getMatrix(), store, notifMgr, idStrategy, httpClient);
|
sessMgr = new SessionManager(cfg.getSession(), cfg.getMatrix(), store, notifMgr);
|
||||||
invMgr = new InvitationManager(cfg, store, idStrategy, keyMgr, signMgr, resolver, notifMgr, pMgr);
|
invMgr = new InvitationManager(cfg, store, idStrategy, keyMgr, signMgr, resolver, notifMgr, pMgr);
|
||||||
authMgr = new AuthManager(cfg, AuthProviders.get(), idStrategy, invMgr, clientDns, httpClient);
|
authMgr = new AuthManager(cfg, AuthProviders.get(), idStrategy, invMgr, clientDns, httpClient);
|
||||||
dirMgr = new DirectoryManager(cfg.getDirectory(), clientDns, httpClient, DirectoryProviders.get());
|
dirMgr = new DirectoryManager(cfg.getDirectory(), clientDns, httpClient, DirectoryProviders.get());
|
||||||
|
@@ -176,10 +176,12 @@ public class AppSvcManager {
|
|||||||
ensureEnabled();
|
ensureEnabled();
|
||||||
|
|
||||||
if (StringUtils.isBlank(token)) {
|
if (StringUtils.isBlank(token)) {
|
||||||
|
log.info("Denying request without a HS token");
|
||||||
throw new HttpMatrixException(401, "M_UNAUTHORIZED", "No HS token");
|
throw new HttpMatrixException(401, "M_UNAUTHORIZED", "No HS token");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!StringUtils.equals(cfg.getEndpoint().getToAS().getToken(), token)) {
|
if (!StringUtils.equals(cfg.getEndpoint().getToAS().getToken(), token)) {
|
||||||
|
log.info("Denying request with an invalid HS token");
|
||||||
throw new NotAllowedException("Invalid HS token");
|
throw new NotAllowedException("Invalid HS token");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -27,7 +27,6 @@ import io.kamax.matrix._MatrixID;
|
|||||||
import io.kamax.matrix._ThreePid;
|
import io.kamax.matrix._ThreePid;
|
||||||
import io.kamax.matrix.client.as.MatrixApplicationServiceClient;
|
import io.kamax.matrix.client.as.MatrixApplicationServiceClient;
|
||||||
import io.kamax.matrix.event.EventKey;
|
import io.kamax.matrix.event.EventKey;
|
||||||
import io.kamax.matrix.hs._MatrixRoom;
|
|
||||||
import io.kamax.mxisd.Mxisd;
|
import io.kamax.mxisd.Mxisd;
|
||||||
import io.kamax.mxisd.backend.sql.synapse.Synapse;
|
import io.kamax.mxisd.backend.sql.synapse.Synapse;
|
||||||
import io.kamax.mxisd.config.MxisdConfig;
|
import io.kamax.mxisd.config.MxisdConfig;
|
||||||
@@ -81,7 +80,7 @@ public class MembershipEventProcessor implements EventTypeProcessor {
|
|||||||
|
|
||||||
_MatrixID target = MatrixID.asAcceptable(targetId);
|
_MatrixID target = MatrixID.asAcceptable(targetId);
|
||||||
if (!StringUtils.equals(target.getDomain(), cfg.getMatrix().getDomain())) {
|
if (!StringUtils.equals(target.getDomain(), cfg.getMatrix().getDomain())) {
|
||||||
log.debug("Ignoring invite for {}: not a local user");
|
log.debug("Ignoring invite for {}: not a local user", targetId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,10 +88,9 @@ public class MembershipEventProcessor implements EventTypeProcessor {
|
|||||||
|
|
||||||
boolean isForMainUser = StringUtils.equals(target.getLocalPart(), cfg.getAppsvc().getUser().getMain());
|
boolean isForMainUser = StringUtils.equals(target.getLocalPart(), cfg.getAppsvc().getUser().getMain());
|
||||||
boolean isForExpInvUser = StringUtils.equals(target.getLocalPart(), cfg.getAppsvc().getUser().getInviteExpired());
|
boolean isForExpInvUser = StringUtils.equals(target.getLocalPart(), cfg.getAppsvc().getUser().getInviteExpired());
|
||||||
boolean isUs = isForMainUser || isForExpInvUser;
|
|
||||||
|
|
||||||
if (StringUtils.equals("join", EventKey.Membership.getStringOrNull(content))) {
|
if (StringUtils.equals("join", EventKey.Membership.getStringOrNull(content))) {
|
||||||
if (!isForMainUser) {
|
if (isForExpInvUser) {
|
||||||
log.warn("We joined the room {} for another identity as the main user, which is not supported. Leaving...", roomId);
|
log.warn("We joined the room {} for another identity as the main user, which is not supported. Leaving...", roomId);
|
||||||
|
|
||||||
client.getUser(target.getLocalPart()).getRoom(roomId).tryLeave().ifPresent(err -> {
|
client.getUser(target.getLocalPart()).getRoom(roomId).tryLeave().ifPresent(err -> {
|
||||||
@@ -108,10 +106,7 @@ public class MembershipEventProcessor implements EventTypeProcessor {
|
|||||||
processForUserIdInvite(roomId, sender, target);
|
processForUserIdInvite(roomId, sender, target);
|
||||||
}
|
}
|
||||||
} else if (StringUtils.equals("leave", EventKey.Membership.getStringOrNull(content))) {
|
} else if (StringUtils.equals("leave", EventKey.Membership.getStringOrNull(content))) {
|
||||||
_MatrixRoom room = client.getRoom(roomId);
|
|
||||||
if (!isUs && room.getJoinedUsers().size() == 1) {
|
|
||||||
// TODO we need to find out if this is only us remaining and leave the room if so, using the right client for it
|
// TODO we need to find out if this is only us remaining and leave the room if so, using the right client for it
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
log.debug("This is not an supported type of membership event, skipping");
|
log.debug("This is not an supported type of membership event, skipping");
|
||||||
}
|
}
|
||||||
|
@@ -64,6 +64,8 @@ import java.util.Objects;
|
|||||||
|
|
||||||
public class AuthManager {
|
public class AuthManager {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(AuthManager.class);
|
||||||
|
|
||||||
private static final String TypeKey = "type";
|
private static final String TypeKey = "type";
|
||||||
private static final String UserKey = "user";
|
private static final String UserKey = "user";
|
||||||
private static final String IdentifierKey = "identifier";
|
private static final String IdentifierKey = "identifier";
|
||||||
@@ -72,7 +74,6 @@ public class AuthManager {
|
|||||||
private static final String UserIdTypeValue = "m.id.user";
|
private static final String UserIdTypeValue = "m.id.user";
|
||||||
private static final String ThreepidTypeValue = "m.id.thirdparty";
|
private static final String ThreepidTypeValue = "m.id.thirdparty";
|
||||||
|
|
||||||
private transient final Logger log = LoggerFactory.getLogger(AuthManager.class);
|
|
||||||
private final Gson gson = GsonUtil.get(); // FIXME replace
|
private final Gson gson = GsonUtil.get(); // FIXME replace
|
||||||
|
|
||||||
private List<AuthenticatorProvider> providers;
|
private List<AuthenticatorProvider> providers;
|
||||||
@@ -138,6 +139,12 @@ public class AuthManager {
|
|||||||
invMgr.publishMappingIfInvited(new ThreePidMapping(pid, mxId));
|
invMgr.publishMappingIfInvited(new ThreePidMapping(pid, mxId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
MatrixID.asValid(mxId);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
log.warn("The returned User ID {} is not a valid Matrix ID. Login might fail at the Homeserver level", mxId);
|
||||||
|
}
|
||||||
|
|
||||||
invMgr.lookupMappingsForInvites();
|
invMgr.lookupMappingsForInvites();
|
||||||
|
|
||||||
return authResult;
|
return authResult;
|
||||||
|
@@ -83,6 +83,12 @@ public class MxisdConfig {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static MxisdConfig forDomain(String domain) {
|
||||||
|
MxisdConfig cfg = new MxisdConfig();
|
||||||
|
cfg.getMatrix().setDomain(domain);
|
||||||
|
return cfg;
|
||||||
|
}
|
||||||
|
|
||||||
private AppServiceConfig appsvc = new AppServiceConfig();
|
private AppServiceConfig appsvc = new AppServiceConfig();
|
||||||
private AuthenticationConfig auth = new AuthenticationConfig();
|
private AuthenticationConfig auth = new AuthenticationConfig();
|
||||||
private DirectoryConfig directory = new DirectoryConfig();
|
private DirectoryConfig directory = new DirectoryConfig();
|
||||||
@@ -309,6 +315,13 @@ public class MxisdConfig {
|
|||||||
this.wordpress = wordpress;
|
this.wordpress = wordpress;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MxisdConfig inMemory() {
|
||||||
|
getKey().setPath(":memory:");
|
||||||
|
getStorage().getProvider().getSqlite().setDatabase(":memory:");
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public MxisdConfig build() {
|
public MxisdConfig build() {
|
||||||
if (StringUtils.isBlank(getServer().getName())) {
|
if (StringUtils.isBlank(getServer().getName())) {
|
||||||
getServer().setName(getMatrix().getDomain());
|
getServer().setName(getMatrix().getDomain());
|
||||||
|
@@ -20,7 +20,6 @@
|
|||||||
|
|
||||||
package io.kamax.mxisd.config.threepid.notification;
|
package io.kamax.mxisd.config.threepid.notification;
|
||||||
|
|
||||||
import com.google.gson.JsonObject;
|
|
||||||
import io.kamax.matrix.ThreePidMedium;
|
import io.kamax.matrix.ThreePidMedium;
|
||||||
import io.kamax.mxisd.threepid.notification.email.EmailRawNotificationHandler;
|
import io.kamax.mxisd.threepid.notification.email.EmailRawNotificationHandler;
|
||||||
import io.kamax.mxisd.threepid.notification.phone.PhoneNotificationHandler;
|
import io.kamax.mxisd.threepid.notification.phone.PhoneNotificationHandler;
|
||||||
@@ -35,7 +34,7 @@ public class NotificationConfig {
|
|||||||
private transient final Logger log = LoggerFactory.getLogger(NotificationConfig.class);
|
private transient final Logger log = LoggerFactory.getLogger(NotificationConfig.class);
|
||||||
|
|
||||||
private Map<String, String> handler = new HashMap<>();
|
private Map<String, String> handler = new HashMap<>();
|
||||||
private Map<String, JsonObject> handlers = new HashMap<>();
|
private Map<String, Object> handlers = new HashMap<>();
|
||||||
|
|
||||||
public NotificationConfig() {
|
public NotificationConfig() {
|
||||||
handler.put(ThreePidMedium.Email.getId(), EmailRawNotificationHandler.ID);
|
handler.put(ThreePidMedium.Email.getId(), EmailRawNotificationHandler.ID);
|
||||||
@@ -50,11 +49,11 @@ public class NotificationConfig {
|
|||||||
this.handler = handler;
|
this.handler = handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, JsonObject> getHandlers() {
|
public Map<String, Object> getHandlers() {
|
||||||
return handlers;
|
return handlers;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setHandlers(Map<String, JsonObject> handlers) {
|
public void setHandlers(Map<String, Object> handlers) {
|
||||||
this.handlers = handlers;
|
this.handlers = handlers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -21,6 +21,7 @@
|
|||||||
package io.kamax.mxisd.crypto;
|
package io.kamax.mxisd.crypto;
|
||||||
|
|
||||||
import io.kamax.mxisd.config.KeyConfig;
|
import io.kamax.mxisd.config.KeyConfig;
|
||||||
|
import io.kamax.mxisd.config.MxisdConfig;
|
||||||
import io.kamax.mxisd.crypto.ed25519.Ed25519KeyManager;
|
import io.kamax.mxisd.crypto.ed25519.Ed25519KeyManager;
|
||||||
import io.kamax.mxisd.crypto.ed25519.Ed25519SignatureManager;
|
import io.kamax.mxisd.crypto.ed25519.Ed25519SignatureManager;
|
||||||
import io.kamax.mxisd.storage.crypto.FileKeyStore;
|
import io.kamax.mxisd.storage.crypto.FileKeyStore;
|
||||||
@@ -54,8 +55,8 @@ public class CryptoFactory {
|
|||||||
return new Ed25519KeyManager(store);
|
return new Ed25519KeyManager(store);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SignatureManager getSignatureManager(Ed25519KeyManager keyMgr) {
|
public static SignatureManager getSignatureManager(MxisdConfig cfg, Ed25519KeyManager keyMgr) {
|
||||||
return new Ed25519SignatureManager(keyMgr);
|
return new Ed25519SignatureManager(cfg, keyMgr);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -22,7 +22,7 @@ package io.kamax.mxisd.crypto;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Types of keys used by an Identity server.
|
* Types of keys used by an Identity server.
|
||||||
* See https://matrix.org/docs/spec/identity_service/r0.1.0.html#key-management
|
* See https://matrix.org/docs/spec/identity_service/r0.2.0.html#key-management
|
||||||
*/
|
*/
|
||||||
public enum KeyType {
|
public enum KeyType {
|
||||||
|
|
||||||
|
@@ -20,12 +20,57 @@
|
|||||||
|
|
||||||
package io.kamax.mxisd.crypto;
|
package io.kamax.mxisd.crypto;
|
||||||
|
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
|
import io.kamax.matrix.event.EventKey;
|
||||||
|
import io.kamax.matrix.json.MatrixJson;
|
||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
public interface SignatureManager {
|
public interface SignatureManager {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sign the message with the default domain and add the signature to the <code>signatures</code> key.
|
||||||
|
* <p>
|
||||||
|
* If the key does not exist yet, it is created. If the key exist, the produced signature will be merged with any
|
||||||
|
* existing ones.
|
||||||
|
*
|
||||||
|
* @param message The message to sign with the default domain and add the produced signature to
|
||||||
|
* @return The provided message with the new signature
|
||||||
|
* @throws IllegalArgumentException If the <code>signatures</code> key exists and its value is not a JSON object
|
||||||
|
*/
|
||||||
|
JsonObject signMessageGson(JsonObject message) throws IllegalArgumentException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sign the message and add the signature to the <code>signatures</code> key.
|
||||||
|
* <p>
|
||||||
|
* If the key does not exist yet, it is created. If the key exist, the produced signature will be merged with any
|
||||||
|
* existing ones.
|
||||||
|
*
|
||||||
|
* @param domain The domain under which the signature should be added
|
||||||
|
* @param message The message to sign and add the produced signature to
|
||||||
|
* @return The provided message with the new signature
|
||||||
|
* @throws IllegalArgumentException If the <code>signatures</code> key exists and its value is not a JSON object
|
||||||
|
*/
|
||||||
|
default JsonObject signMessageGson(String domain, JsonObject message) throws IllegalArgumentException {
|
||||||
|
JsonElement signEl = message.remove(EventKey.Signatures.get());
|
||||||
|
JsonObject oldSigns = new JsonObject();
|
||||||
|
if (!Objects.isNull(signEl)) {
|
||||||
|
if (!signEl.isJsonObject()) {
|
||||||
|
throw new IllegalArgumentException("Message contains a signatures key that is not a JSON object value");
|
||||||
|
}
|
||||||
|
|
||||||
|
oldSigns = signEl.getAsJsonObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonObject newSigns = signMessageGson(domain, MatrixJson.encodeCanonical(message));
|
||||||
|
oldSigns.entrySet().forEach(entry -> newSigns.add(entry.getKey(), entry.getValue()));
|
||||||
|
message.add(EventKey.Signatures.get(), newSigns);
|
||||||
|
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sign the message and produce a <code>signatures</code> object that can directly be added to the object being signed.
|
* Sign the message and produce a <code>signatures</code> object that can directly be added to the object being signed.
|
||||||
*
|
*
|
||||||
|
@@ -23,6 +23,8 @@ package io.kamax.mxisd.crypto.ed25519;
|
|||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import io.kamax.matrix.codec.MxBase64;
|
import io.kamax.matrix.codec.MxBase64;
|
||||||
import io.kamax.matrix.json.MatrixJson;
|
import io.kamax.matrix.json.MatrixJson;
|
||||||
|
import io.kamax.mxisd.config.MxisdConfig;
|
||||||
|
import io.kamax.mxisd.config.ServerConfig;
|
||||||
import io.kamax.mxisd.crypto.KeyIdentifier;
|
import io.kamax.mxisd.crypto.KeyIdentifier;
|
||||||
import io.kamax.mxisd.crypto.Signature;
|
import io.kamax.mxisd.crypto.Signature;
|
||||||
import io.kamax.mxisd.crypto.SignatureManager;
|
import io.kamax.mxisd.crypto.SignatureManager;
|
||||||
@@ -35,12 +37,19 @@ import java.security.SignatureException;
|
|||||||
|
|
||||||
public class Ed25519SignatureManager implements SignatureManager {
|
public class Ed25519SignatureManager implements SignatureManager {
|
||||||
|
|
||||||
|
private final ServerConfig cfg;
|
||||||
private final Ed25519KeyManager keyMgr;
|
private final Ed25519KeyManager keyMgr;
|
||||||
|
|
||||||
public Ed25519SignatureManager(Ed25519KeyManager keyMgr) {
|
public Ed25519SignatureManager(MxisdConfig cfg, Ed25519KeyManager keyMgr) {
|
||||||
|
this.cfg = cfg.getServer();
|
||||||
this.keyMgr = keyMgr;
|
this.keyMgr = keyMgr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JsonObject signMessageGson(JsonObject message) throws IllegalArgumentException {
|
||||||
|
return signMessageGson(cfg.getName(), message);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JsonObject signMessageGson(String domain, String message) {
|
public JsonObject signMessageGson(String domain, String message) {
|
||||||
Signature sign = sign(message);
|
Signature sign = sign(message);
|
||||||
|
@@ -33,8 +33,7 @@ public class InternalServerError extends HttpMatrixException {
|
|||||||
super(
|
super(
|
||||||
HttpStatus.SC_INTERNAL_SERVER_ERROR,
|
HttpStatus.SC_INTERNAL_SERVER_ERROR,
|
||||||
"M_UNKNOWN",
|
"M_UNKNOWN",
|
||||||
"An internal server error occured. If this error persists, please contact support with reference #" +
|
"An internal server error occurred. Contact your administrator with reference Transaction #" + Instant.now().toEpochMilli()
|
||||||
Instant.now().toEpochMilli()
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -97,7 +97,7 @@ public class SaneHandler extends BasicHttpHandler {
|
|||||||
if (StringUtils.isNotBlank(e.getInternalReason())) {
|
if (StringUtils.isNotBlank(e.getInternalReason())) {
|
||||||
log.error("Transaction #{} - {}", e.getReference(), e.getInternalReason());
|
log.error("Transaction #{} - {}", e.getReference(), e.getInternalReason());
|
||||||
} else {
|
} else {
|
||||||
log.error("Transaction #{}", e);
|
log.error("Transaction #{}", e.getReference(), e);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleException(exchange, e);
|
handleException(exchange, e);
|
||||||
|
@@ -36,7 +36,7 @@ public class RestAuthHandler extends BasicHttpHandler {
|
|||||||
|
|
||||||
public static final String Path = "/_matrix-internal/identity/v1/check_credentials";
|
public static final String Path = "/_matrix-internal/identity/v1/check_credentials";
|
||||||
|
|
||||||
private transient final Logger log = LoggerFactory.getLogger(RestAuthHandler.class);
|
private static final Logger log = LoggerFactory.getLogger(RestAuthHandler.class);
|
||||||
|
|
||||||
private AuthManager mgr;
|
private AuthManager mgr;
|
||||||
|
|
||||||
@@ -45,7 +45,7 @@ public class RestAuthHandler extends BasicHttpHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleRequest(HttpServerExchange exchange) throws Exception {
|
public void handleRequest(HttpServerExchange exchange) {
|
||||||
JsonObject authData = parseJsonObject(exchange, "user");
|
JsonObject authData = parseJsonObject(exchange, "user");
|
||||||
if (!authData.has("id") || !authData.has("password")) {
|
if (!authData.has("id") || !authData.has("password")) {
|
||||||
throw new JsonMemberNotFoundException("Missing id or password keys");
|
throw new JsonMemberNotFoundException("Missing id or password keys");
|
||||||
|
@@ -40,7 +40,7 @@ public class UserDirectorySearchHandler extends HomeserverProxyHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleRequest(HttpServerExchange exchange) throws Exception {
|
public void handleRequest(HttpServerExchange exchange) {
|
||||||
String accessToken = getAccessToken(exchange);
|
String accessToken = getAccessToken(exchange);
|
||||||
UserDirectorySearchRequest searchQuery = parseJsonTo(exchange, UserDirectorySearchRequest.class);
|
UserDirectorySearchRequest searchQuery = parseJsonTo(exchange, UserDirectorySearchRequest.class);
|
||||||
URI target = URI.create(exchange.getRequestURL());
|
URI target = URI.create(exchange.getRequestURL());
|
||||||
|
@@ -37,7 +37,7 @@ public class BulkLookupHandler extends LookupHandler {
|
|||||||
|
|
||||||
public static final String Path = IsAPIv1.Base + "/bulk_lookup";
|
public static final String Path = IsAPIv1.Base + "/bulk_lookup";
|
||||||
|
|
||||||
private transient final Logger log = LoggerFactory.getLogger(SingleLookupHandler.class);
|
private static final Logger log = LoggerFactory.getLogger(SingleLookupHandler.class);
|
||||||
|
|
||||||
private LookupStrategy strategy;
|
private LookupStrategy strategy;
|
||||||
|
|
||||||
|
@@ -31,7 +31,7 @@ public class EphemeralKeyIsValidHandler extends KeyIsValidHandler {
|
|||||||
|
|
||||||
public static final String Path = IsAPIv1.Base + "/pubkey/ephemeral/isvalid";
|
public static final String Path = IsAPIv1.Base + "/pubkey/ephemeral/isvalid";
|
||||||
|
|
||||||
private transient final Logger log = LoggerFactory.getLogger(EphemeralKeyIsValidHandler.class);
|
private static final Logger log = LoggerFactory.getLogger(EphemeralKeyIsValidHandler.class);
|
||||||
|
|
||||||
private KeyManager mgr;
|
private KeyManager mgr;
|
||||||
|
|
||||||
|
@@ -21,11 +21,15 @@
|
|||||||
package io.kamax.mxisd.http.undertow.handler.identity.v1;
|
package io.kamax.mxisd.http.undertow.handler.identity.v1;
|
||||||
|
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
|
import io.kamax.matrix.json.GsonUtil;
|
||||||
|
import io.kamax.mxisd.crypto.SignatureManager;
|
||||||
import io.kamax.mxisd.exception.BadRequestException;
|
import io.kamax.mxisd.exception.BadRequestException;
|
||||||
import io.kamax.mxisd.http.IsAPIv1;
|
import io.kamax.mxisd.http.IsAPIv1;
|
||||||
import io.kamax.mxisd.http.io.identity.BindRequest;
|
import io.kamax.mxisd.http.io.identity.BindRequest;
|
||||||
|
import io.kamax.mxisd.http.io.identity.SingeLookupReplyJson;
|
||||||
import io.kamax.mxisd.http.undertow.handler.BasicHttpHandler;
|
import io.kamax.mxisd.http.undertow.handler.BasicHttpHandler;
|
||||||
import io.kamax.mxisd.invitation.InvitationManager;
|
import io.kamax.mxisd.invitation.InvitationManager;
|
||||||
|
import io.kamax.mxisd.lookup.SingleLookupReply;
|
||||||
import io.kamax.mxisd.session.SessionManager;
|
import io.kamax.mxisd.session.SessionManager;
|
||||||
import io.undertow.server.HttpServerExchange;
|
import io.undertow.server.HttpServerExchange;
|
||||||
import io.undertow.util.QueryParameterUtils;
|
import io.undertow.util.QueryParameterUtils;
|
||||||
@@ -42,14 +46,16 @@ public class SessionTpidBindHandler extends BasicHttpHandler {
|
|||||||
|
|
||||||
public static final String Path = IsAPIv1.Base + "/3pid/bind";
|
public static final String Path = IsAPIv1.Base + "/3pid/bind";
|
||||||
|
|
||||||
private transient final Logger log = LoggerFactory.getLogger(SessionTpidBindHandler.class);
|
private static final Logger log = LoggerFactory.getLogger(SessionTpidBindHandler.class);
|
||||||
|
|
||||||
private SessionManager mgr;
|
private SessionManager mgr;
|
||||||
private InvitationManager invMgr;
|
private InvitationManager invMgr;
|
||||||
|
private SignatureManager signMgr;
|
||||||
|
|
||||||
public SessionTpidBindHandler(SessionManager mgr, InvitationManager invMgr) {
|
public SessionTpidBindHandler(SessionManager mgr, InvitationManager invMgr, SignatureManager signMgr) {
|
||||||
this.mgr = mgr;
|
this.mgr = mgr;
|
||||||
this.invMgr = invMgr;
|
this.invMgr = invMgr;
|
||||||
|
this.signMgr = signMgr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -74,8 +80,9 @@ public class SessionTpidBindHandler extends BasicHttpHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
mgr.bind(bindReq.getSid(), bindReq.getSecret(), bindReq.getUserId());
|
SingleLookupReply lookup = mgr.bind(bindReq.getSid(), bindReq.getSecret(), bindReq.getUserId());
|
||||||
respond(exchange, new JsonObject());
|
JsonObject response = signMgr.signMessageGson(GsonUtil.makeObj(new SingeLookupReplyJson(lookup)));
|
||||||
|
respond(exchange, response);
|
||||||
} catch (BadRequestException e) {
|
} catch (BadRequestException e) {
|
||||||
log.info("requested session was not validated");
|
log.info("requested session was not validated");
|
||||||
|
|
||||||
|
@@ -21,15 +21,22 @@
|
|||||||
package io.kamax.mxisd.http.undertow.handler.identity.v1;
|
package io.kamax.mxisd.http.undertow.handler.identity.v1;
|
||||||
|
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
|
import io.kamax.mxisd.exception.BadRequestException;
|
||||||
|
import io.kamax.mxisd.exception.NotAllowedException;
|
||||||
import io.kamax.mxisd.http.IsAPIv1;
|
import io.kamax.mxisd.http.IsAPIv1;
|
||||||
import io.kamax.mxisd.http.undertow.handler.BasicHttpHandler;
|
import io.kamax.mxisd.http.undertow.handler.BasicHttpHandler;
|
||||||
import io.kamax.mxisd.session.SessionManager;
|
import io.kamax.mxisd.session.SessionManager;
|
||||||
import io.undertow.server.HttpServerExchange;
|
import io.undertow.server.HttpServerExchange;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
public class SessionTpidUnbindHandler extends BasicHttpHandler {
|
public class SessionTpidUnbindHandler extends BasicHttpHandler {
|
||||||
|
|
||||||
public static final String Path = IsAPIv1.Base + "/3pid/unbind";
|
public static final String Path = IsAPIv1.Base + "/3pid/unbind";
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(SessionTpidUnbindHandler.class);
|
||||||
|
|
||||||
private final SessionManager sessionMgr;
|
private final SessionManager sessionMgr;
|
||||||
|
|
||||||
public SessionTpidUnbindHandler(SessionManager sessionMgr) {
|
public SessionTpidUnbindHandler(SessionManager sessionMgr) {
|
||||||
@@ -38,6 +45,18 @@ public class SessionTpidUnbindHandler extends BasicHttpHandler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleRequest(HttpServerExchange exchange) {
|
public void handleRequest(HttpServerExchange exchange) {
|
||||||
|
String auth = exchange.getRequestHeaders().getFirst("Authorization");
|
||||||
|
if (StringUtils.isNotEmpty(auth)) {
|
||||||
|
// We have a auth header to process
|
||||||
|
if (StringUtils.startsWith(auth, "X-Matrix ")) {
|
||||||
|
log.warn("A remote host attempted to unbind without proper authorization. Request was denied");
|
||||||
|
log.warn("See https://github.com/kamax-matrix/mxisd/wiki/mxisd-and-your-privacy for more info");
|
||||||
|
throw new NotAllowedException("3PID can only be removed via 3PID sessions, not via Homeserver signature");
|
||||||
|
} else {
|
||||||
|
throw new BadRequestException("Illegal authorization type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
JsonObject body = parseJsonObject(exchange);
|
JsonObject body = parseJsonObject(exchange);
|
||||||
sessionMgr.unbind(body);
|
sessionMgr.unbind(body);
|
||||||
writeBodyAsUtf8(exchange, "{}");
|
writeBodyAsUtf8(exchange, "{}");
|
||||||
|
@@ -21,9 +21,7 @@
|
|||||||
package io.kamax.mxisd.http.undertow.handler.identity.v1;
|
package io.kamax.mxisd.http.undertow.handler.identity.v1;
|
||||||
|
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import io.kamax.matrix.event.EventKey;
|
|
||||||
import io.kamax.matrix.json.GsonUtil;
|
import io.kamax.matrix.json.GsonUtil;
|
||||||
import io.kamax.matrix.json.MatrixJson;
|
|
||||||
import io.kamax.mxisd.config.MxisdConfig;
|
import io.kamax.mxisd.config.MxisdConfig;
|
||||||
import io.kamax.mxisd.config.ServerConfig;
|
import io.kamax.mxisd.config.ServerConfig;
|
||||||
import io.kamax.mxisd.crypto.SignatureManager;
|
import io.kamax.mxisd.crypto.SignatureManager;
|
||||||
@@ -73,11 +71,8 @@ public class SingleLookupHandler extends LookupHandler {
|
|||||||
respondJson(exchange, "{}");
|
respondJson(exchange, "{}");
|
||||||
} else {
|
} else {
|
||||||
SingleLookupReply lookup = lookupOpt.get();
|
SingleLookupReply lookup = lookupOpt.get();
|
||||||
|
|
||||||
// FIXME signing should be done in the business model, not in the controller
|
|
||||||
JsonObject obj = GsonUtil.makeObj(new SingeLookupReplyJson(lookup));
|
JsonObject obj = GsonUtil.makeObj(new SingeLookupReplyJson(lookup));
|
||||||
obj.add(EventKey.Signatures.get(), signMgr.signMessageGson(cfg.getName(), MatrixJson.encodeCanonical(obj)));
|
signMgr.signMessageGson(cfg.getName(), obj);
|
||||||
|
|
||||||
respondJson(exchange, obj);
|
respondJson(exchange, obj);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -111,11 +111,11 @@ public class InvitationManager {
|
|||||||
this.notifMgr = notifMgr;
|
this.notifMgr = notifMgr;
|
||||||
this.profileMgr = profileMgr;
|
this.profileMgr = profileMgr;
|
||||||
|
|
||||||
log.info("Loading saved invites");
|
log.debug("Loading saved invites");
|
||||||
Collection<ThreePidInviteIO> ioList = storage.getInvites();
|
Collection<ThreePidInviteIO> ioList = storage.getInvites();
|
||||||
ioList.forEach(io -> {
|
ioList.forEach(io -> {
|
||||||
io.getProperties().putIfAbsent(CreatedAtPropertyKey, defaultCreateTs);
|
io.getProperties().putIfAbsent(CreatedAtPropertyKey, defaultCreateTs);
|
||||||
log.info("Processing invite {}", GsonUtil.get().toJson(io));
|
log.debug("Processing invite {}", GsonUtil.get().toJson(io));
|
||||||
ThreePidInvite invite = new ThreePidInvite(
|
ThreePidInvite invite = new ThreePidInvite(
|
||||||
MatrixID.asAcceptable(io.getSender()),
|
MatrixID.asAcceptable(io.getSender()),
|
||||||
io.getMedium(),
|
io.getMedium(),
|
||||||
@@ -127,6 +127,7 @@ public class InvitationManager {
|
|||||||
ThreePidInviteReply reply = new ThreePidInviteReply(io.getId(), invite, io.getToken(), "", Collections.emptyList());
|
ThreePidInviteReply reply = new ThreePidInviteReply(io.getId(), invite, io.getToken(), "", Collections.emptyList());
|
||||||
invitations.put(reply.getId(), reply);
|
invitations.put(reply.getId(), reply);
|
||||||
});
|
});
|
||||||
|
log.info("Loaded saved invites");
|
||||||
|
|
||||||
// FIXME export such madness into matrix-java-sdk with a nice wrapper to talk to a homeserver
|
// FIXME export such madness into matrix-java-sdk with a nice wrapper to talk to a homeserver
|
||||||
try {
|
try {
|
||||||
@@ -511,6 +512,9 @@ public class InvitationManager {
|
|||||||
publishMapping(reply, lookup.getMxid().getId());
|
publishMapping(reply, lookup.getMxid().getId());
|
||||||
} else {
|
} else {
|
||||||
log.info("No mapping for pending invite {}", getIdForLog(reply));
|
log.info("No mapping for pending invite {}", getIdForLog(reply));
|
||||||
|
if (lookupMgr.getLocalProviders().isEmpty()) {
|
||||||
|
log.warn("No Identity store has been configured, this invite may never resolve");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
log.error("Unable to process invite", t);
|
log.error("Unable to process invite", t);
|
||||||
|
@@ -72,7 +72,7 @@ public class SingleLookupReply {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public SingleLookupReply(SingleLookupRequest request, _MatrixID mxid) {
|
public SingleLookupReply(SingleLookupRequest request, _MatrixID mxid) {
|
||||||
this(request, mxid, Instant.now(), Instant.ofEpochMilli(0), Instant.ofEpochMilli(253402300799000L));
|
this(request, mxid, Instant.now(), Instant.now().minusSeconds(60), Instant.now().plusSeconds(5 * 60));
|
||||||
}
|
}
|
||||||
|
|
||||||
public SingleLookupReply(SingleLookupRequest request, _MatrixID mxid, Instant timestamp, Instant notBefore, Instant notAfter) {
|
public SingleLookupReply(SingleLookupRequest request, _MatrixID mxid, Instant timestamp, Instant notBefore, Instant notAfter) {
|
||||||
|
@@ -79,7 +79,7 @@ public class IdentityServerUtils {
|
|||||||
|
|
||||||
JsonElement el = parser.parse(IOUtils.toString(res.getEntity().getContent(), StandardCharsets.UTF_8));
|
JsonElement el = parser.parse(IOUtils.toString(res.getEntity().getContent(), StandardCharsets.UTF_8));
|
||||||
if (!el.isJsonObject()) {
|
if (!el.isJsonObject()) {
|
||||||
log.debug("IS {} did not send back an empty JSON object as per spec, not a valid IS");
|
log.debug("IS {} did not send back an empty JSON object as per spec, not a valid IS", remote);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,7 +90,7 @@ public class IdentityServerUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getSrvRecordName(String domain) {
|
private static String getSrvRecordName(String domain) {
|
||||||
return "_matrix-identity._tcp." + domain;
|
return "_matrix-identity._tcp." + domain;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -27,20 +27,19 @@ import io.kamax.matrix._MatrixID;
|
|||||||
import io.kamax.matrix.json.GsonUtil;
|
import io.kamax.matrix.json.GsonUtil;
|
||||||
import io.kamax.mxisd.config.MatrixConfig;
|
import io.kamax.mxisd.config.MatrixConfig;
|
||||||
import io.kamax.mxisd.config.SessionConfig;
|
import io.kamax.mxisd.config.SessionConfig;
|
||||||
|
import io.kamax.mxisd.exception.BadRequestException;
|
||||||
import io.kamax.mxisd.exception.NotAllowedException;
|
import io.kamax.mxisd.exception.NotAllowedException;
|
||||||
import io.kamax.mxisd.exception.NotImplementedException;
|
|
||||||
import io.kamax.mxisd.exception.SessionNotValidatedException;
|
import io.kamax.mxisd.exception.SessionNotValidatedException;
|
||||||
import io.kamax.mxisd.exception.SessionUnknownException;
|
import io.kamax.mxisd.exception.SessionUnknownException;
|
||||||
import io.kamax.mxisd.lookup.SingleLookupReply;
|
import io.kamax.mxisd.lookup.SingleLookupReply;
|
||||||
|
import io.kamax.mxisd.lookup.SingleLookupRequest;
|
||||||
import io.kamax.mxisd.lookup.ThreePidValidation;
|
import io.kamax.mxisd.lookup.ThreePidValidation;
|
||||||
import io.kamax.mxisd.lookup.strategy.LookupStrategy;
|
|
||||||
import io.kamax.mxisd.notification.NotificationManager;
|
import io.kamax.mxisd.notification.NotificationManager;
|
||||||
import io.kamax.mxisd.storage.IStorage;
|
import io.kamax.mxisd.storage.IStorage;
|
||||||
import io.kamax.mxisd.storage.dao.IThreePidSessionDao;
|
import io.kamax.mxisd.storage.dao.IThreePidSessionDao;
|
||||||
import io.kamax.mxisd.threepid.session.ThreePidSession;
|
import io.kamax.mxisd.threepid.session.ThreePidSession;
|
||||||
import org.apache.commons.lang3.RandomStringUtils;
|
import org.apache.commons.lang3.RandomStringUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.http.impl.client.CloseableHttpClient;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@@ -56,25 +55,17 @@ public class SessionManager {
|
|||||||
private MatrixConfig mxCfg;
|
private MatrixConfig mxCfg;
|
||||||
private IStorage storage;
|
private IStorage storage;
|
||||||
private NotificationManager notifMgr;
|
private NotificationManager notifMgr;
|
||||||
private LookupStrategy lookupMgr;
|
|
||||||
|
|
||||||
// FIXME export into central class, set version
|
|
||||||
private CloseableHttpClient client;
|
|
||||||
|
|
||||||
public SessionManager(
|
public SessionManager(
|
||||||
SessionConfig cfg,
|
SessionConfig cfg,
|
||||||
MatrixConfig mxCfg,
|
MatrixConfig mxCfg,
|
||||||
IStorage storage,
|
IStorage storage,
|
||||||
NotificationManager notifMgr,
|
NotificationManager notifMgr
|
||||||
LookupStrategy lookupMgr,
|
|
||||||
CloseableHttpClient client
|
|
||||||
) {
|
) {
|
||||||
this.cfg = cfg;
|
this.cfg = cfg;
|
||||||
this.mxCfg = mxCfg;
|
this.mxCfg = mxCfg;
|
||||||
this.storage = storage;
|
this.storage = storage;
|
||||||
this.notifMgr = notifMgr;
|
this.notifMgr = notifMgr;
|
||||||
this.lookupMgr = lookupMgr;
|
|
||||||
this.client = client;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private ThreePidSession getSession(String sid, String secret) {
|
private ThreePidSession getSession(String sid, String secret) {
|
||||||
@@ -128,7 +119,7 @@ public class SessionManager {
|
|||||||
log.info("Generated new session {} to validate {} from server {}", sessionId, tpid, server);
|
log.info("Generated new session {} to validate {} from server {}", sessionId, tpid, server);
|
||||||
|
|
||||||
storage.insertThreePidSession(session.getDao());
|
storage.insertThreePidSession(session.getDao());
|
||||||
log.info("Stored session {}", sessionId, tpid, server);
|
log.info("Stored session {}", sessionId);
|
||||||
|
|
||||||
log.info("Session {} for {}: sending validation notification", sessionId, tpid);
|
log.info("Session {} for {}: sending validation notification", sessionId, tpid);
|
||||||
notifMgr.sendForValidation(session);
|
notifMgr.sendForValidation(session);
|
||||||
@@ -157,7 +148,7 @@ public class SessionManager {
|
|||||||
return new ThreePidValidation(session.getThreePid(), session.getValidationTime());
|
return new ThreePidValidation(session.getThreePid(), session.getValidationTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void bind(String sid, String secret, String mxidRaw) {
|
public SingleLookupReply bind(String sid, String secret, String mxidRaw) {
|
||||||
// We make sure we have an acceptable User ID
|
// We make sure we have an acceptable User ID
|
||||||
if (StringUtils.isEmpty(mxidRaw)) {
|
if (StringUtils.isEmpty(mxidRaw)) {
|
||||||
throw new IllegalArgumentException("No Matrix User ID provided");
|
throw new IllegalArgumentException("No Matrix User ID provided");
|
||||||
@@ -171,60 +162,45 @@ public class SessionManager {
|
|||||||
|
|
||||||
// Only accept binds if the domain matches our own
|
// Only accept binds if the domain matches our own
|
||||||
if (!StringUtils.equalsIgnoreCase(mxCfg.getDomain(), mxid.getDomain())) {
|
if (!StringUtils.equalsIgnoreCase(mxCfg.getDomain(), mxid.getDomain())) {
|
||||||
throw new NotAllowedException("Only Matrix IDs from domain " + mxCfg + " can be bound");
|
throw new NotAllowedException("Only Matrix IDs from domain " + mxCfg.getDomain() + " can be bound");
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info("Session {}: Binding of {}:{} to Matrix ID {} is accepted",
|
log.info("Session {}: Binding of {}:{} to Matrix ID {} is accepted",
|
||||||
session.getId(), session.getThreePid().getMedium(), session.getThreePid().getAddress(), mxid.getId());
|
session.getId(), session.getThreePid().getMedium(), session.getThreePid().getAddress(), mxid.getId());
|
||||||
|
|
||||||
|
SingleLookupRequest request = new SingleLookupRequest();
|
||||||
|
request.setType(session.getThreePid().getMedium());
|
||||||
|
request.setThreePid(session.getThreePid().getAddress());
|
||||||
|
return new SingleLookupReply(request, mxid);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void unbind(JsonObject reqData) {
|
public void unbind(JsonObject reqData) {
|
||||||
if (reqData.entrySet().size() == 2 && reqData.has("mxid") && reqData.has("threepid")) {
|
_MatrixID mxid;
|
||||||
/* This is a HS request to remove a 3PID and is considered:
|
|
||||||
* - An attack on user privacy
|
|
||||||
* - A baffling spec breakage requiring IS and HS 3PID info to be independent [1]
|
|
||||||
* - A baffling spec breakage that 3PID (un)bind is only one way [2]
|
|
||||||
*
|
|
||||||
* Given the lack of response on our extensive feedback on the proposal [3] which has not landed in the spec yet [4],
|
|
||||||
* We'll be denying such unbind requests and will inform users using their 3PID that a fraudulent attempt of
|
|
||||||
* removing their 3PID binding has been attempted and blocked.
|
|
||||||
*
|
|
||||||
* [1]: https://matrix.org/docs/spec/client_server/r0.4.0.html#adding-account-administrative-contact-information
|
|
||||||
* [2]: https://matrix.org/docs/spec/identity_service/r0.1.0.html#privacy
|
|
||||||
* [3]: https://docs.google.com/document/d/135g2muVxmuml0iUnLoTZxk8M2ZSt3kJzg81chGh51yg/edit
|
|
||||||
* [4]: https://github.com/matrix-org/matrix-doc/issues/1194
|
|
||||||
*/
|
|
||||||
|
|
||||||
log.warn("A remote host attempted to unbind without proper authorization. Request was denied");
|
|
||||||
|
|
||||||
if (!cfg.getPolicy().getUnbind().getFraudulent().getSendWarning()) {
|
|
||||||
log.info("Not sending notification to 3PID owner as per configuration");
|
|
||||||
} else {
|
|
||||||
log.info("Sending notification to 3PID owner as per configuration");
|
|
||||||
|
|
||||||
ThreePid tpid = GsonUtil.get().fromJson(GsonUtil.getObj(reqData, "threepid"), ThreePid.class);
|
|
||||||
Optional<SingleLookupReply> lookup = lookupMgr.findLocal(tpid.getMedium(), tpid.getAddress());
|
|
||||||
if (!lookup.isPresent()) {
|
|
||||||
log.info("No 3PID owner found, not sending any notification");
|
|
||||||
} else {
|
|
||||||
log.info("3PID owner found, sending notification");
|
|
||||||
try {
|
try {
|
||||||
notifMgr.sendForFraudulentUnbind(tpid);
|
mxid = MatrixID.asAcceptable(GsonUtil.getStringOrThrow(reqData, "mxid"));
|
||||||
log.info("Notification sent");
|
} catch (IllegalArgumentException e) {
|
||||||
} catch (NotImplementedException e) {
|
throw new BadRequestException(e.getMessage());
|
||||||
log.warn("Unable to send notification: {}", e.getMessage());
|
|
||||||
} catch (RuntimeException e) {
|
|
||||||
log.warn("Unable to send notification due to unknown error. See stacktrace below", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new NotAllowedException("You have attempted to alter 3PID bindings, which can only be done by the 3PID owner directly. " +
|
String sid = GsonUtil.getStringOrNull(reqData, "sid");
|
||||||
"We have informed the 3PID owner of your fraudulent attempt.");
|
String secret = GsonUtil.getStringOrNull(reqData, "client_secret");
|
||||||
|
ThreePid tpid = GsonUtil.get().fromJson(GsonUtil.getObj(reqData, "threepid"), ThreePid.class);
|
||||||
|
|
||||||
|
// We ensure the session was validated
|
||||||
|
ThreePidSession session = getSessionIfValidated(sid, secret);
|
||||||
|
|
||||||
|
// As per spec, we can only allow if the provided 3PID matches the validated session
|
||||||
|
if (!session.getThreePid().equals(tpid)) {
|
||||||
|
throw new BadRequestException("3PID to unbind does not match the one from the validated session");
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info("Denying unbind request as the endpoint is not defined in the spec.");
|
// We only allow unbind for the domain we manage, mirroring bind
|
||||||
throw new NotAllowedException(499, "This endpoint does not exist in the spec and therefore is not supported.");
|
if (!StringUtils.equalsIgnoreCase(mxCfg.getDomain(), mxid.getDomain())) {
|
||||||
|
throw new NotAllowedException("Only Matrix IDs from domain " + mxCfg.getDomain() + " can be unbound");
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("Session {}: Unbinding of {}:{} to Matrix ID {} is accepted",
|
||||||
|
session.getId(), session.getThreePid().getMedium(), session.getThreePid().getAddress(), mxid.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -21,10 +21,12 @@
|
|||||||
package io.kamax.mxisd.threepid.connector.phone;
|
package io.kamax.mxisd.threepid.connector.phone;
|
||||||
|
|
||||||
import com.twilio.Twilio;
|
import com.twilio.Twilio;
|
||||||
|
import com.twilio.exception.ApiException;
|
||||||
import com.twilio.rest.api.v2010.account.Message;
|
import com.twilio.rest.api.v2010.account.Message;
|
||||||
import com.twilio.type.PhoneNumber;
|
import com.twilio.type.PhoneNumber;
|
||||||
import io.kamax.mxisd.config.threepid.connector.PhoneTwilioConfig;
|
import io.kamax.mxisd.config.threepid.connector.PhoneTwilioConfig;
|
||||||
import io.kamax.mxisd.exception.BadRequestException;
|
import io.kamax.mxisd.exception.InternalServerError;
|
||||||
|
import io.kamax.mxisd.exception.NotImplementedException;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -52,12 +54,17 @@ public class PhoneSmsTwilioConnector implements PhoneConnector {
|
|||||||
@Override
|
@Override
|
||||||
public void send(String recipient, String content) {
|
public void send(String recipient, String content) {
|
||||||
if (StringUtils.isBlank(cfg.getAccountSid()) || StringUtils.isBlank(cfg.getAuthToken()) || StringUtils.isBlank(cfg.getNumber())) {
|
if (StringUtils.isBlank(cfg.getAccountSid()) || StringUtils.isBlank(cfg.getAuthToken()) || StringUtils.isBlank(cfg.getNumber())) {
|
||||||
throw new BadRequestException("Phone numbers cannot be validated at this time. Contact your administrator.");
|
log.error("Twilio connector in not fully configured and is missing mandatory configuration values.");
|
||||||
|
throw new NotImplementedException("Phone numbers cannot be validated at this time. Contact your administrator.");
|
||||||
}
|
}
|
||||||
|
|
||||||
recipient = "+" + recipient;
|
recipient = "+" + recipient;
|
||||||
log.info("Sending SMS notification from {} to {} with {} characters", cfg.getNumber(), recipient, content.length());
|
log.info("Sending SMS notification from {} to {} with {} characters", cfg.getNumber(), recipient, content.length());
|
||||||
|
try {
|
||||||
Message.creator(new PhoneNumber("+" + recipient), new PhoneNumber(cfg.getNumber()), content).create();
|
Message.creator(new PhoneNumber("+" + recipient), new PhoneNumber(cfg.getNumber()), content).create();
|
||||||
|
} catch (ApiException e) {
|
||||||
|
throw new InternalServerError(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -27,8 +27,9 @@ import io.kamax.mxisd.http.IsAPIv1;
|
|||||||
import io.kamax.mxisd.invitation.IMatrixIdInvite;
|
import io.kamax.mxisd.invitation.IMatrixIdInvite;
|
||||||
import io.kamax.mxisd.invitation.IThreePidInviteReply;
|
import io.kamax.mxisd.invitation.IThreePidInviteReply;
|
||||||
import io.kamax.mxisd.threepid.session.IThreePidSession;
|
import io.kamax.mxisd.threepid.session.IThreePidSession;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import io.kamax.mxisd.util.RestClientUtils;
|
||||||
import org.apache.commons.lang.WordUtils;
|
import org.apache.commons.lang.WordUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import static io.kamax.mxisd.http.io.identity.StoreInviteRequest.Keys.RoomName;
|
import static io.kamax.mxisd.http.io.identity.StoreInviteRequest.Keys.RoomName;
|
||||||
import static io.kamax.mxisd.http.io.identity.StoreInviteRequest.Keys.SenderDisplayName;
|
import static io.kamax.mxisd.http.io.identity.StoreInviteRequest.Keys.SenderDisplayName;
|
||||||
@@ -46,16 +47,26 @@ public abstract class PlaceholderNotificationGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected String populateForCommon(ThreePid recipient, String input) {
|
protected String populateForCommon(ThreePid recipient, String input) {
|
||||||
|
if (StringUtils.isBlank(input)) {
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
String domainPretty = WordUtils.capitalizeFully(mxCfg.getDomain());
|
String domainPretty = WordUtils.capitalizeFully(mxCfg.getDomain());
|
||||||
|
|
||||||
return input
|
return input
|
||||||
.replace("%DOMAIN%", mxCfg.getDomain())
|
.replace("%DOMAIN%", mxCfg.getDomain())
|
||||||
.replace("%DOMAIN_PRETTY%", domainPretty)
|
.replace("%DOMAIN_PRETTY%", domainPretty)
|
||||||
.replace("%RECIPIENT_MEDIUM%", recipient.getMedium())
|
.replace("%RECIPIENT_MEDIUM%", recipient.getMedium())
|
||||||
.replace("%RECIPIENT_ADDRESS%", recipient.getAddress());
|
.replace("%RECIPIENT_MEDIUM_URL_ENCODED%", RestClientUtils.urlEncode(recipient.getMedium()))
|
||||||
|
.replace("%RECIPIENT_ADDRESS%", recipient.getAddress())
|
||||||
|
.replace("%RECIPIENT_ADDRESS_URL_ENCODED%", RestClientUtils.urlEncode(recipient.getAddress()));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String populateForInvite(IMatrixIdInvite invite, String input) {
|
protected String populateForInvite(IMatrixIdInvite invite, String input) {
|
||||||
|
if (StringUtils.isBlank(input)) {
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
String senderName = invite.getProperties().getOrDefault(SenderDisplayName, "");
|
String senderName = invite.getProperties().getOrDefault(SenderDisplayName, "");
|
||||||
String senderNameOrId = StringUtils.defaultIfBlank(senderName, invite.getSender().getId());
|
String senderNameOrId = StringUtils.defaultIfBlank(senderName, invite.getSender().getId());
|
||||||
String roomName = invite.getProperties().getOrDefault(RoomName, "");
|
String roomName = invite.getProperties().getOrDefault(RoomName, "");
|
||||||
@@ -72,6 +83,10 @@ public abstract class PlaceholderNotificationGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected String populateForReply(IThreePidInviteReply invite, String input) {
|
protected String populateForReply(IThreePidInviteReply invite, String input) {
|
||||||
|
if (StringUtils.isBlank(input)) {
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
ThreePid tpid = new ThreePid(invite.getInvite().getMedium(), invite.getInvite().getAddress());
|
ThreePid tpid = new ThreePid(invite.getInvite().getMedium(), invite.getInvite().getAddress());
|
||||||
|
|
||||||
String senderName = invite.getInvite().getProperties().getOrDefault(SenderDisplayName, "");
|
String senderName = invite.getInvite().getProperties().getOrDefault(SenderDisplayName, "");
|
||||||
@@ -86,13 +101,19 @@ public abstract class PlaceholderNotificationGenerator {
|
|||||||
.replace("%SENDER_NAME%", senderName)
|
.replace("%SENDER_NAME%", senderName)
|
||||||
.replace("%SENDER_NAME_OR_ID%", senderNameOrId)
|
.replace("%SENDER_NAME_OR_ID%", senderNameOrId)
|
||||||
.replace("%INVITE_MEDIUM%", tpid.getMedium())
|
.replace("%INVITE_MEDIUM%", tpid.getMedium())
|
||||||
|
.replace("%INVITE_MEDIUM_URL_ENCODED%", RestClientUtils.urlEncode(tpid.getMedium()))
|
||||||
.replace("%INVITE_ADDRESS%", tpid.getAddress())
|
.replace("%INVITE_ADDRESS%", tpid.getAddress())
|
||||||
|
.replace("%INVITE_ADDRESS_URL_ENCODED%", RestClientUtils.urlEncode(tpid.getAddress()))
|
||||||
.replace("%ROOM_ID%", invite.getInvite().getRoomId())
|
.replace("%ROOM_ID%", invite.getInvite().getRoomId())
|
||||||
.replace("%ROOM_NAME%", roomName)
|
.replace("%ROOM_NAME%", roomName)
|
||||||
.replace("%ROOM_NAME_OR_ID%", roomNameOrId);
|
.replace("%ROOM_NAME_OR_ID%", roomNameOrId);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String populateForValidation(IThreePidSession session, String input) {
|
protected String populateForValidation(IThreePidSession session, String input) {
|
||||||
|
if (StringUtils.isBlank(input)) {
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
String validationLink = srvCfg.getPublicUrl() + IsAPIv1.getValidate(
|
String validationLink = srvCfg.getPublicUrl() + IsAPIv1.getValidate(
|
||||||
session.getThreePid().getMedium(),
|
session.getThreePid().getMedium(),
|
||||||
session.getId(),
|
session.getId(),
|
||||||
|
@@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
package io.kamax.mxisd.threepid.notification;
|
package io.kamax.mxisd.threepid.notification;
|
||||||
|
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonSyntaxException;
|
||||||
import io.kamax.matrix.ThreePidMedium;
|
import io.kamax.matrix.ThreePidMedium;
|
||||||
import io.kamax.matrix.json.GsonUtil;
|
import io.kamax.matrix.json.GsonUtil;
|
||||||
import io.kamax.mxisd.Mxisd;
|
import io.kamax.mxisd.Mxisd;
|
||||||
@@ -65,13 +65,18 @@ public class BuiltInNotificationHandlerSupplier implements NotificationHandlerSu
|
|||||||
if (StringUtils.equals(EmailRawNotificationHandler.ID, handler)) {
|
if (StringUtils.equals(EmailRawNotificationHandler.ID, handler)) {
|
||||||
Object o = mxisd.getConfig().getThreepid().getMedium().get(ThreePidMedium.Email.getId());
|
Object o = mxisd.getConfig().getThreepid().getMedium().get(ThreePidMedium.Email.getId());
|
||||||
if (Objects.nonNull(o)) {
|
if (Objects.nonNull(o)) {
|
||||||
EmailConfig emailCfg = GsonUtil.get().fromJson(GsonUtil.makeObj(o), EmailConfig.class);
|
EmailConfig emailCfg;
|
||||||
|
try {
|
||||||
|
emailCfg = GsonUtil.get().fromJson(GsonUtil.makeObj(o), EmailConfig.class);
|
||||||
|
} catch (JsonSyntaxException e) {
|
||||||
|
throw new ConfigurationException("Invalid configuration for threepid email notification");
|
||||||
|
}
|
||||||
|
|
||||||
if (org.apache.commons.lang.StringUtils.isBlank(emailCfg.getGenerator())) {
|
if (StringUtils.isBlank(emailCfg.getGenerator())) {
|
||||||
throw new ConfigurationException("notification.email.generator");
|
throw new ConfigurationException("notification.email.generator");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (org.apache.commons.lang.StringUtils.isBlank(emailCfg.getConnector())) {
|
if (StringUtils.isBlank(emailCfg.getConnector())) {
|
||||||
throw new ConfigurationException("notification.email.connector");
|
throw new ConfigurationException("notification.email.connector");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,9 +99,15 @@ public class BuiltInNotificationHandlerSupplier implements NotificationHandlerSu
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (StringUtils.equals(EmailSendGridNotificationHandler.ID, handler)) {
|
if (StringUtils.equals(EmailSendGridNotificationHandler.ID, handler)) {
|
||||||
JsonObject cfgJson = mxisd.getConfig().getNotification().getHandlers().get(EmailSendGridNotificationHandler.ID);
|
Object cfgJson = mxisd.getConfig().getNotification().getHandlers().get(EmailSendGridNotificationHandler.ID);
|
||||||
if (Objects.nonNull(cfgJson)) {
|
if (Objects.nonNull(cfgJson)) {
|
||||||
EmailSendGridConfig cfg = GsonUtil.get().fromJson(cfgJson, EmailSendGridConfig.class);
|
EmailSendGridConfig cfg;
|
||||||
|
try {
|
||||||
|
cfg = GsonUtil.get().fromJson(GsonUtil.get().toJson(cfgJson), EmailSendGridConfig.class);
|
||||||
|
} catch (JsonSyntaxException e) {
|
||||||
|
throw new ConfigurationException("Invalid configuration for threepid email sendgrid handler");
|
||||||
|
}
|
||||||
|
|
||||||
NotificationHandlers.register(() -> new EmailSendGridNotificationHandler(mxisd.getConfig(), cfg));
|
NotificationHandlers.register(() -> new EmailSendGridNotificationHandler(mxisd.getConfig(), cfg));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -107,7 +118,12 @@ public class BuiltInNotificationHandlerSupplier implements NotificationHandlerSu
|
|||||||
if (StringUtils.equals(PhoneNotificationHandler.ID, handler)) {
|
if (StringUtils.equals(PhoneNotificationHandler.ID, handler)) {
|
||||||
Object o = mxisd.getConfig().getThreepid().getMedium().get(ThreePidMedium.PhoneNumber.getId());
|
Object o = mxisd.getConfig().getThreepid().getMedium().get(ThreePidMedium.PhoneNumber.getId());
|
||||||
if (Objects.nonNull(o)) {
|
if (Objects.nonNull(o)) {
|
||||||
PhoneConfig cfg = GsonUtil.get().fromJson(GsonUtil.makeObj(o), PhoneConfig.class);
|
PhoneConfig cfg;
|
||||||
|
try {
|
||||||
|
cfg = GsonUtil.get().fromJson(GsonUtil.makeObj(o), PhoneConfig.class);
|
||||||
|
} catch (JsonSyntaxException e) {
|
||||||
|
throw new ConfigurationException("Invalid configuration for threepid msisdn notification");
|
||||||
|
}
|
||||||
|
|
||||||
List<PhoneGenerator> generators = StreamSupport
|
List<PhoneGenerator> generators = StreamSupport
|
||||||
.stream(ServiceLoader.load(PhoneGeneratorSupplier.class).spliterator(), false)
|
.stream(ServiceLoader.load(PhoneGeneratorSupplier.class).spliterator(), false)
|
||||||
|
@@ -33,7 +33,7 @@ import io.kamax.mxisd.notification.NotificationHandler;
|
|||||||
import io.kamax.mxisd.threepid.generator.PlaceholderNotificationGenerator;
|
import io.kamax.mxisd.threepid.generator.PlaceholderNotificationGenerator;
|
||||||
import io.kamax.mxisd.threepid.session.IThreePidSession;
|
import io.kamax.mxisd.threepid.session.IThreePidSession;
|
||||||
import io.kamax.mxisd.util.FileUtil;
|
import io.kamax.mxisd.util.FileUtil;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@@ -86,6 +86,9 @@ public class EmailSendGridNotificationHandler extends PlaceholderNotificationGen
|
|||||||
@Override
|
@Override
|
||||||
public void sendForInvite(IMatrixIdInvite invite) {
|
public void sendForInvite(IMatrixIdInvite invite) {
|
||||||
EmailTemplate template = cfg.getTemplates().getGeneric().get("matrixId");
|
EmailTemplate template = cfg.getTemplates().getGeneric().get("matrixId");
|
||||||
|
if (StringUtils.isAllBlank(template.getBody().getText(), template.getBody().getHtml())) {
|
||||||
|
throw new FeatureNotAvailable("No template has been configured for Matrix ID invite notifications");
|
||||||
|
}
|
||||||
|
|
||||||
Email email = getEmail();
|
Email email = getEmail();
|
||||||
email.setSubject(populateForInvite(invite, template.getSubject()));
|
email.setSubject(populateForInvite(invite, template.getSubject()));
|
||||||
@@ -98,6 +101,10 @@ public class EmailSendGridNotificationHandler extends PlaceholderNotificationGen
|
|||||||
@Override
|
@Override
|
||||||
public void sendForReply(IThreePidInviteReply invite) {
|
public void sendForReply(IThreePidInviteReply invite) {
|
||||||
EmailTemplate template = cfg.getTemplates().getInvite();
|
EmailTemplate template = cfg.getTemplates().getInvite();
|
||||||
|
if (StringUtils.isAllBlank(template.getBody().getText(), template.getBody().getHtml())) {
|
||||||
|
throw new FeatureNotAvailable("No template has been configured for 3PID invite notifications");
|
||||||
|
}
|
||||||
|
|
||||||
Email email = getEmail();
|
Email email = getEmail();
|
||||||
email.setSubject(populateForReply(invite, template.getSubject()));
|
email.setSubject(populateForReply(invite, template.getSubject()));
|
||||||
email.setText(populateForReply(invite, getFromFile(template.getBody().getText())));
|
email.setText(populateForReply(invite, getFromFile(template.getBody().getText())));
|
||||||
@@ -109,6 +116,10 @@ public class EmailSendGridNotificationHandler extends PlaceholderNotificationGen
|
|||||||
@Override
|
@Override
|
||||||
public void sendForValidation(IThreePidSession session) {
|
public void sendForValidation(IThreePidSession session) {
|
||||||
EmailTemplate template = cfg.getTemplates().getSession().getValidation();
|
EmailTemplate template = cfg.getTemplates().getSession().getValidation();
|
||||||
|
if (StringUtils.isAllBlank(template.getBody().getText(), template.getBody().getHtml())) {
|
||||||
|
throw new FeatureNotAvailable("No template has been configured for validation notifications");
|
||||||
|
}
|
||||||
|
|
||||||
Email email = getEmail();
|
Email email = getEmail();
|
||||||
email.setSubject(populateForValidation(session, template.getSubject()));
|
email.setSubject(populateForValidation(session, template.getSubject()));
|
||||||
email.setText(populateForValidation(session, getFromFile(template.getBody().getText())));
|
email.setText(populateForValidation(session, getFromFile(template.getBody().getText())));
|
||||||
@@ -120,6 +131,10 @@ public class EmailSendGridNotificationHandler extends PlaceholderNotificationGen
|
|||||||
@Override
|
@Override
|
||||||
public void sendForFraudulentUnbind(ThreePid tpid) {
|
public void sendForFraudulentUnbind(ThreePid tpid) {
|
||||||
EmailTemplate template = cfg.getTemplates().getSession().getUnbind().getFraudulent();
|
EmailTemplate template = cfg.getTemplates().getSession().getUnbind().getFraudulent();
|
||||||
|
if (StringUtils.isAllBlank(template.getBody().getText(), template.getBody().getHtml())) {
|
||||||
|
throw new FeatureNotAvailable("No template has been configured for fraudulent unbind notifications");
|
||||||
|
}
|
||||||
|
|
||||||
Email email = getEmail();
|
Email email = getEmail();
|
||||||
email.setSubject(populateForCommon(tpid, template.getSubject()));
|
email.setSubject(populateForCommon(tpid, template.getSubject()));
|
||||||
email.setText(populateForCommon(tpid, getFromFile(template.getBody().getText())));
|
email.setText(populateForCommon(tpid, getFromFile(template.getBody().getText())));
|
||||||
|
@@ -25,12 +25,22 @@ import org.apache.http.client.methods.HttpPost;
|
|||||||
import org.apache.http.entity.ContentType;
|
import org.apache.http.entity.ContentType;
|
||||||
import org.apache.http.entity.StringEntity;
|
import org.apache.http.entity.StringEntity;
|
||||||
|
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.net.URLEncoder;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
public class RestClientUtils {
|
public class RestClientUtils {
|
||||||
|
|
||||||
private static Gson gson = GsonUtil.build();
|
private static Gson gson = GsonUtil.build();
|
||||||
|
|
||||||
|
public static String urlEncode(String value) {
|
||||||
|
try {
|
||||||
|
return URLEncoder.encode(value, StandardCharsets.UTF_8.name());
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static HttpPost post(String url, String body) {
|
public static HttpPost post(String url, String body) {
|
||||||
StringEntity entity = new StringEntity(body, StandardCharsets.UTF_8);
|
StringEntity entity = new StringEntity(body, StandardCharsets.UTF_8);
|
||||||
entity.setContentType(ContentType.APPLICATION_JSON.toString());
|
entity.setContentType(ContentType.APPLICATION_JSON.toString());
|
||||||
|
@@ -9,19 +9,12 @@ Content-Disposition: inline
|
|||||||
|
|
||||||
Hi,
|
Hi,
|
||||||
|
|
||||||
%SENDER_NAME_OR_ID% has invited you into a room [%ROOM_NAME_OR_ID%] on
|
%SENDER_NAME_OR_ID% has invited you to the Matrix room [%ROOM_NAME_OR_ID%].
|
||||||
Matrix. To join the conversation, register an account on %REGISTER_URL%
|
To join the conversation, register an account using %REGISTER_URL%
|
||||||
|
|
||||||
|
You may be required to provide the same email used for this invite during registration.
|
||||||
You can also register an account on a public server and get in touch with them.
|
You can also register an account on a public server and get in touch with them.
|
||||||
|
|
||||||
|
|
||||||
About Matrix:
|
|
||||||
|
|
||||||
Matrix is an open standard for interoperable, decentralised, real-time communication
|
|
||||||
over IP, supporting group chat, file transfer, voice and video calling, integrations to
|
|
||||||
other apps, bridges to other communication systems and much more. It can be used to power
|
|
||||||
Instant Messaging, VoIP/WebRTC signalling, Internet of Things communication.
|
|
||||||
|
|
||||||
Thanks,
|
Thanks,
|
||||||
|
|
||||||
%DOMAIN_PRETTY% Admins
|
%DOMAIN_PRETTY% Admins
|
||||||
@@ -39,26 +32,26 @@ Content-Disposition: inline
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
body {
|
body {
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
pre, code {
|
pre, code {
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
#page {
|
#page {
|
||||||
font-family: 'Open Sans', Helvetica, Arial, Sans-Serif;
|
font-family: 'Open Sans', Helvetica, Arial, Sans-Serif;
|
||||||
font-color: #454545;
|
font-color: #454545;
|
||||||
font-size: 12pt;
|
font-size: 12pt;
|
||||||
width: 100%%;
|
width: 100%%;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#inner {
|
#inner {
|
||||||
width: 640px;
|
width: 640px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@@ -66,24 +59,17 @@ pre, code {
|
|||||||
<tr>
|
<tr>
|
||||||
<td> </td>
|
<td> </td>
|
||||||
<td id="inner">
|
<td id="inner">
|
||||||
<p>Hi,</p>
|
<p>Hi,</p>
|
||||||
|
|
||||||
<p>%SENDER_NAME_OR_ID% has invited you into a room [%ROOM_NAME_OR_ID%] on
|
<p>%SENDER_NAME_OR_ID% has invited you to the Matrix room [%ROOM_NAME_OR_ID%].<br/>
|
||||||
Matrix. To join the conversation, register an account on <a href="%REGISTER_URL%">%DOMAIN%</a>.</p>
|
To join the conversation, register an account on <a href="%REGISTER_URL%">%DOMAIN%</a>.</p>
|
||||||
|
|
||||||
<pYou can also register an account on a public server and get in touch with them.</p>
|
<p>You may be required to provide the same email used for this invite during registration.<br/>
|
||||||
|
You can also register an account on a public server and get in touch with them.</p>
|
||||||
|
|
||||||
<br>
|
<p>Thanks,</p>
|
||||||
<p>About Matrix:</p>
|
|
||||||
|
|
||||||
<p>Matrix is an open standard for interoperable, decentralised, real-time communication
|
<p>%DOMAIN_PRETTY% Admins</p>
|
||||||
over IP, supporting group chat, file transfer, voice and video calling, integrations to
|
|
||||||
other apps, bridges to other communication systems and much more. It can be used to power
|
|
||||||
Instant Messaging, VoIP/WebRTC signalling, Internet of Things communication.</p>
|
|
||||||
|
|
||||||
<p>Thanks,</p>
|
|
||||||
|
|
||||||
<p>%DOMAIN_PRETTY% Admins</p>
|
|
||||||
</td>
|
</td>
|
||||||
<td> </td>
|
<td> </td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@@ -9,7 +9,7 @@ Content-Disposition: inline
|
|||||||
|
|
||||||
Hi,
|
Hi,
|
||||||
|
|
||||||
%SENDER_NAME_OR_ID% has invited you into a room [%ROOM_NAME_OR_ID%] on Matrix.
|
%SENDER_NAME_OR_ID% has invited you to the Matrix room [%ROOM_NAME_OR_ID%].
|
||||||
|
|
||||||
Thanks,
|
Thanks,
|
||||||
|
|
||||||
@@ -28,26 +28,26 @@ Content-Disposition: inline
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
body {
|
body {
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
pre, code {
|
pre, code {
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
#page {
|
#page {
|
||||||
font-family: 'Open Sans', Helvetica, Arial, Sans-Serif;
|
font-family: 'Open Sans', Helvetica, Arial, Sans-Serif;
|
||||||
font-color: #454545;
|
font-color: #454545;
|
||||||
font-size: 12pt;
|
font-size: 12pt;
|
||||||
width: 100%%;
|
width: 100%%;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#inner {
|
#inner {
|
||||||
width: 640px;
|
width: 640px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@@ -55,13 +55,13 @@ pre, code {
|
|||||||
<tr>
|
<tr>
|
||||||
<td> </td>
|
<td> </td>
|
||||||
<td id="inner">
|
<td id="inner">
|
||||||
<p>Hi,</p>
|
<p>Hi,</p>
|
||||||
|
|
||||||
<p>%SENDER_NAME_OR_ID% has invited you into a room [%ROOM_NAME_OR_ID%] on Matrix.</p>
|
<p>%SENDER_NAME_OR_ID% has invited you to the Matrix room [%ROOM_NAME_OR_ID%].</p>
|
||||||
|
|
||||||
<p>Thanks,</p>
|
<p>Thanks,</p>
|
||||||
|
|
||||||
<p>%DOMAIN_PRETTY% Admins</p>
|
<p>%DOMAIN_PRETTY% Admins</p>
|
||||||
</td>
|
</td>
|
||||||
<td> </td>
|
<td> </td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@@ -61,26 +61,26 @@ Content-Disposition: inline
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
body {
|
body {
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
pre, code {
|
pre, code {
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
#page {
|
#page {
|
||||||
font-family: 'Open Sans', Helvetica, Arial, Sans-Serif;
|
font-family: 'Open Sans', Helvetica, Arial, Sans-Serif;
|
||||||
font-color: #454545;
|
font-color: #454545;
|
||||||
font-size: 12pt;
|
font-size: 12pt;
|
||||||
width: 100%%;
|
width: 100%%;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#inner {
|
#inner {
|
||||||
width: 640px;
|
width: 640px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@@ -88,42 +88,42 @@ pre, code {
|
|||||||
<tr>
|
<tr>
|
||||||
<td> </td>
|
<td> </td>
|
||||||
<td id="inner">
|
<td id="inner">
|
||||||
<p>Hi,</p>
|
<p>Hi,</p>
|
||||||
|
|
||||||
<p><b>THIS IS IMPORTANT, PLEASE READ CAREFULLY</b>.<br/>
|
<p><b>THIS IS IMPORTANT, PLEASE READ CAREFULLY</b>.<br/>
|
||||||
If you are the system administrator of the Matrix installation, read the second section.</p>
|
If you are the system administrator of the Matrix installation, read the second section.</p>
|
||||||
|
|
||||||
<p>This is a notification email that a possibly unauthorized entity has attempted to alter your
|
<p>This is a notification email that a possibly unauthorized entity has attempted to alter your
|
||||||
3PIDs (email, phone numbers, etc.) settings. The request was denied and no change has been made.</p>
|
3PIDs (email, phone numbers, etc.) settings. The request was denied and no change has been made.</p>
|
||||||
|
|
||||||
<p>This is so you are aware of a possible failure in case you just tried to remove a 3PID from your account.</p>
|
<p>This is so you are aware of a possible failure in case you just tried to remove a 3PID from your account.</p>
|
||||||
|
|
||||||
<p>If you do not understand this email, please forward it to your System administrator.</p>
|
<p>If you do not understand this email, please forward it to your System administrator.</p>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
<p>As the system administrator:</p>
|
<p>As the system administrator:</p>
|
||||||
|
|
||||||
<p>If you are using synapse as a Homeserver, this is a known issue related to <a href="https://github.com/matrix-org/matrix-doc/issues/1194">MSC1194</a>
|
<p>If you are using synapse as a Homeserver, this is a known issue related to <a href="https://github.com/matrix-org/matrix-doc/issues/1194">MSC1194</a>
|
||||||
and abuse of separation of concerns. As a privacy-centric product and to protect your privacy, the request was actively
|
and abuse of separation of concerns. As a privacy-centric product and to protect your privacy, the request was actively
|
||||||
blocked. We have written a more detailed explanation on our <a href="https://github.com/kamax-matrix/mxisd/wiki/mxisd-and-your-privacy">Privacy wiki page</a>
|
blocked. We have written a more detailed explanation on our <a href="https://github.com/kamax-matrix/mxisd/wiki/mxisd-and-your-privacy">Privacy wiki page</a>
|
||||||
(<a href="https://github.com/kamax-matrix/mxisd/wiki/mxisd-and-your-privacy#msc1194-synapse-and-impacts-on-your-privacy">Direct link to section</a>)
|
(<a href="https://github.com/kamax-matrix/mxisd/wiki/mxisd-and-your-privacy#msc1194-synapse-and-impacts-on-your-privacy">Direct link to section</a>)
|
||||||
so you can fully grasp the impact for you and your users.</p>
|
so you can fully grasp the impact for you and your users.</p>
|
||||||
|
|
||||||
<p>We have open an issue on the synapse repos to reflect the related privacy concerns and GDPR violation(s) and would
|
<p>We have open an issue on the synapse repos to reflect the related privacy concerns and GDPR violation(s) and would
|
||||||
appreciate if you could comment on it or simply adds a thumbs up so the concerns are finally dealt with by the synapse dev team.<br/>
|
appreciate if you could comment on it or simply adds a thumbs up so the concerns are finally dealt with by the synapse dev team.<br/>
|
||||||
Issue: <a href="https://github.com/matrix-org/synapse/issues/4540">https://github.com/matrix-org/synapse/issues/4540</a></p>
|
Issue: <a href="https://github.com/matrix-org/synapse/issues/4540">https://github.com/matrix-org/synapse/issues/4540</a></p>
|
||||||
|
|
||||||
<p>If you are using another Homeserver or this came following no action from your own users, then you have been the target
|
<p>If you are using another Homeserver or this came following no action from your own users, then you have been the target
|
||||||
of an unbind attack from a rogue entity which was blocked. You may want to check your logs to see the exact source of
|
of an unbind attack from a rogue entity which was blocked. You may want to check your logs to see the exact source of
|
||||||
the attack and take relevant actions following your policy.</p>
|
the attack and take relevant actions following your policy.</p>
|
||||||
|
|
||||||
<p>If you would like to disable these notifications, please see the
|
<p>If you would like to disable these notifications, please see the
|
||||||
<a href="https://github.com/kamax-matrix/mxisd/blob/master/docs/threepids/session/session.md#configuration">3PID sessions configuration documentation.</a></p>
|
<a href="https://github.com/kamax-matrix/mxisd/blob/master/docs/threepids/session/session.md#configuration">3PID sessions configuration documentation.</a></p>
|
||||||
|
|
||||||
<p>Thanks,</p>
|
<p>Thanks,</p>
|
||||||
|
|
||||||
<p>%DOMAIN_PRETTY% Admins</p>
|
<p>%DOMAIN_PRETTY% Admins</p>
|
||||||
</td>
|
</td>
|
||||||
<td> </td>
|
<td> </td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@@ -33,30 +33,30 @@ Content-Disposition: inline
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
body {
|
body {
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
pre, code {
|
pre, code {
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
#page {
|
#page {
|
||||||
font-family: 'Open Sans', Helvetica, Arial, Sans-Serif;
|
font-family: 'Open Sans', Helvetica, Arial, Sans-Serif;
|
||||||
font-color: #454545;
|
font-color: #454545;
|
||||||
font-size: 12pt;
|
font-size: 12pt;
|
||||||
width: 100%%;
|
width: 100%%;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#inner {
|
#inner {
|
||||||
width: 640px;
|
width: 640px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.notif_link a, .footer a {
|
.notif_link a, .footer a {
|
||||||
color: #76CFA6 ! important;
|
color: #76CFA6 ! important;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
@@ -33,11 +33,7 @@ public class MxisdDefaultTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void defaultConfig() {
|
public void defaultConfig() {
|
||||||
MxisdConfig cfg = new MxisdConfig();
|
MxisdConfig cfg = MxisdConfig.forDomain(domain).inMemory();
|
||||||
cfg.getMatrix().setDomain(domain);
|
|
||||||
cfg.getKey().setPath(":memory:");
|
|
||||||
cfg.getStorage().getProvider().getSqlite().setDatabase(":memory:");
|
|
||||||
|
|
||||||
Mxisd m = new Mxisd(cfg);
|
Mxisd m = new Mxisd(cfg);
|
||||||
m.start();
|
m.start();
|
||||||
|
|
||||||
|
@@ -41,10 +41,7 @@ public class MxisdTest {
|
|||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void before() {
|
public void before() {
|
||||||
MxisdConfig cfg = new MxisdConfig();
|
MxisdConfig cfg = MxisdConfig.forDomain("localhost").inMemory();
|
||||||
cfg.getMatrix().setDomain("localhost");
|
|
||||||
cfg.getKey().setPath(":memory:");
|
|
||||||
cfg.getStorage().getProvider().getSqlite().setDatabase(":memory:");
|
|
||||||
|
|
||||||
MemoryThreePid mem3pid = new MemoryThreePid();
|
MemoryThreePid mem3pid = new MemoryThreePid();
|
||||||
mem3pid.setMedium("email");
|
mem3pid.setMedium("email");
|
||||||
|
77
src/test/java/io/kamax/mxisd/test/auth/AuthManagerTest.java
Normal file
77
src/test/java/io/kamax/mxisd/test/auth/AuthManagerTest.java
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* mxisd - Matrix Identity Server Daemon
|
||||||
|
* Copyright (C) 2019 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.test.auth;
|
||||||
|
|
||||||
|
import io.kamax.mxisd.Mxisd;
|
||||||
|
import io.kamax.mxisd.auth.AuthManager;
|
||||||
|
import io.kamax.mxisd.auth.UserAuthResult;
|
||||||
|
import io.kamax.mxisd.config.MxisdConfig;
|
||||||
|
import io.kamax.mxisd.config.memory.MemoryIdentityConfig;
|
||||||
|
import io.kamax.mxisd.config.memory.MemoryThreePid;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
public class AuthManagerTest {
|
||||||
|
|
||||||
|
private static AuthManager mgr;
|
||||||
|
|
||||||
|
// FIXME we should be able to easily build the class ourselves
|
||||||
|
// FIXME use constants
|
||||||
|
@BeforeClass
|
||||||
|
public static void beforeClass() {
|
||||||
|
MxisdConfig cfg = new MxisdConfig();
|
||||||
|
cfg.getMatrix().setDomain("localhost");
|
||||||
|
cfg.getKey().setPath(":memory:");
|
||||||
|
cfg.getStorage().getProvider().getSqlite().setDatabase(":memory:");
|
||||||
|
|
||||||
|
MemoryThreePid mem3pid = new MemoryThreePid();
|
||||||
|
mem3pid.setMedium("email");
|
||||||
|
mem3pid.setAddress("john@localhost");
|
||||||
|
MemoryIdentityConfig validCfg = new MemoryIdentityConfig();
|
||||||
|
validCfg.setUsername("john");
|
||||||
|
validCfg.setPassword("doe");
|
||||||
|
validCfg.getThreepids().add(mem3pid);
|
||||||
|
MemoryIdentityConfig illegalUser = new MemoryIdentityConfig();
|
||||||
|
illegalUser.setUsername("JANE");
|
||||||
|
illegalUser.setPassword("doe");
|
||||||
|
cfg.getMemory().setEnabled(true);
|
||||||
|
cfg.getMemory().getIdentities().add(validCfg);
|
||||||
|
cfg.getMemory().getIdentities().add(illegalUser);
|
||||||
|
|
||||||
|
Mxisd m = new Mxisd(cfg);
|
||||||
|
m.start();
|
||||||
|
mgr = m.getAuth();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void basic() {
|
||||||
|
UserAuthResult result = mgr.authenticate("@john:localhost", "doe");
|
||||||
|
assertTrue(result.isSuccess());
|
||||||
|
|
||||||
|
// For backward-compatibility as per instructed by the spec, we do not fail on an illegal username
|
||||||
|
// This makes sure we don't break it
|
||||||
|
result = mgr.authenticate("@JANE:localhost", "doe");
|
||||||
|
assertTrue(result.isSuccess());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -21,8 +21,10 @@
|
|||||||
package io.kamax.mxisd.test.crypto;
|
package io.kamax.mxisd.test.crypto;
|
||||||
|
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
|
import io.kamax.matrix.event.EventKey;
|
||||||
import io.kamax.matrix.json.GsonUtil;
|
import io.kamax.matrix.json.GsonUtil;
|
||||||
import io.kamax.matrix.json.MatrixJson;
|
import io.kamax.matrix.json.MatrixJson;
|
||||||
|
import io.kamax.mxisd.config.MxisdConfig;
|
||||||
import io.kamax.mxisd.crypto.Signature;
|
import io.kamax.mxisd.crypto.Signature;
|
||||||
import io.kamax.mxisd.crypto.SignatureManager;
|
import io.kamax.mxisd.crypto.SignatureManager;
|
||||||
import io.kamax.mxisd.crypto.ed25519.Ed25519Key;
|
import io.kamax.mxisd.crypto.ed25519.Ed25519Key;
|
||||||
@@ -36,10 +38,14 @@ import org.junit.Test;
|
|||||||
|
|
||||||
import static org.hamcrest.core.Is.is;
|
import static org.hamcrest.core.Is.is;
|
||||||
import static org.hamcrest.core.IsEqual.equalTo;
|
import static org.hamcrest.core.IsEqual.equalTo;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertThat;
|
import static org.junit.Assert.assertThat;
|
||||||
|
|
||||||
public class SignatureManagerTest {
|
public class SignatureManagerTest {
|
||||||
|
|
||||||
|
private static final String lookupData = "{\n" + " \"not_before\": 0,\n" + " \"address\": \"mxisd-federation-test@kamax.io\",\n"
|
||||||
|
+ " \"medium\": \"email\",\n" + " \"mxid\": \"@mxisd-lookup-test:kamax.io\",\n"
|
||||||
|
+ " \"not_after\": 253402300799000,\n" + " \"ts\": 1523482030147\n" + "}";
|
||||||
private static SignatureManager signMgr;
|
private static SignatureManager signMgr;
|
||||||
|
|
||||||
private static SignatureManager build(String keySeed) {
|
private static SignatureManager build(String keySeed) {
|
||||||
@@ -47,7 +53,7 @@ public class SignatureManagerTest {
|
|||||||
KeyStore store = new MemoryKeyStore();
|
KeyStore store = new MemoryKeyStore();
|
||||||
store.add(key);
|
store.add(key);
|
||||||
|
|
||||||
return new Ed25519SignatureManager(new Ed25519KeyManager(store));
|
return new Ed25519SignatureManager(MxisdConfig.forDomain("localhost").inMemory().build(), new Ed25519KeyManager(store));
|
||||||
}
|
}
|
||||||
|
|
||||||
@BeforeClass
|
@BeforeClass
|
||||||
@@ -98,12 +104,19 @@ public class SignatureManagerTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void onIdentityLookup() {
|
public void onIdentityLookup() {
|
||||||
String value = MatrixJson.encodeCanonical("{\n" + " \"address\": \"mxisd-federation-test@kamax.io\",\n"
|
String value = MatrixJson.encodeCanonical(lookupData);
|
||||||
+ " \"medium\": \"email\",\n" + " \"mxid\": \"@mxisd-lookup-test:kamax.io\",\n"
|
|
||||||
+ " \"not_after\": 253402300799000,\n" + " \"not_before\": 0,\n" + " \"ts\": 1523482030147\n" + "}");
|
|
||||||
|
|
||||||
String sign = "ObKA4PNQh2g6c7Yo2QcTcuDgIwhknG7ZfqmNYzbhrbLBOqZomU22xX9raufN2Y3ke1FXsDqsGs7WBDodmzZJCg";
|
String sign = "ObKA4PNQh2g6c7Yo2QcTcuDgIwhknG7ZfqmNYzbhrbLBOqZomU22xX9raufN2Y3ke1FXsDqsGs7WBDodmzZJCg";
|
||||||
testSign(value, sign);
|
testSign(value, sign);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onIdentityLookupFull() {
|
||||||
|
JsonObject data = GsonUtil.parseObj(lookupData);
|
||||||
|
signMgr.signMessageGson("localhost", data);
|
||||||
|
JsonObject signatures = EventKey.Signatures.getObj(data);
|
||||||
|
JsonObject domainSign = GsonUtil.getObj(signatures, "localhost");
|
||||||
|
String sign = GsonUtil.getStringOrThrow(domainSign, "ed25519:0");
|
||||||
|
assertEquals(sign, "ObKA4PNQh2g6c7Yo2QcTcuDgIwhknG7ZfqmNYzbhrbLBOqZomU22xX9raufN2Y3ke1FXsDqsGs7WBDodmzZJCg");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* mxisd - Matrix Identity Server Daemon
|
||||||
|
* Copyright (C) 2019 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.test.util;
|
||||||
|
|
||||||
|
import io.kamax.mxisd.util.RestClientUtils;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
public class RestClientUtilsTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void urlEncode() {
|
||||||
|
String encoded = RestClientUtils.urlEncode("john+doe@example.org");
|
||||||
|
assertEquals("john%2Bdoe%40example.org", encoded);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Reference in New Issue
Block a user