Compare commits

..

21 Commits

Author SHA1 Message Date
adrnam
61fec4aec7 3PID authentication (#60)
Fix for #49
2018-03-08 18:29:03 +01:00
Max Dor
1db76139a9 Invite resolution enhancements (#63)
* Make invite resolution process configurable

* Add warning in logs if invite resolution is not recursive
2018-03-05 10:00:09 +01:00
Max Dor
a27858082c Add support for NetIQ as a LDAP backend (#61) 2018-03-03 00:28:15 +01:00
Maxime Dor
ea08a80504 Respect 3PID session policy for remote sending with phone numbers 2018-03-02 23:27:19 +01:00
Maxime Dor
cb3130d365 Send phone number in response body when creating 3PID session
If missing, Riot will show "undefined" instead of the number.
2018-03-02 23:26:33 +01:00
Maxime Dor
7189a4b100 Prepare for kamax-io/matrix-java-sdk#23 2018-02-27 18:30:36 +01:00
adrnam
f71cdbf83e Clarified configuration comments (#54) 2018-02-23 19:40:08 +01:00
Maxime Dor
665a284f4b Clarify wording 2018-02-21 17:22:11 +01:00
Maxime Dor
5e142eb41d Add some more LDAP debug entries 2018-01-28 18:02:03 +01:00
Maxime Dor
9fede41904 Add documentation for nginx config 2018-01-21 14:36:44 +01:00
Maxime Dor
5871bb6609 Set proper backend for Synapse SQL 2018-01-18 23:40:46 +01:00
Maxime Dor
5dbaca643a Fix #42 2017-12-31 13:02:41 +01:00
Maxime Dor
bf9576f9c3 Optimize Dockerfile statements order for caching 2017-12-25 08:54:38 +01:00
Maxime Dor
773f38d349 Properly mark REST Directory provider as component (Fix #48) 2017-12-23 17:43:03 +01:00
kiorky
6a5a4b3c1c Fix Docker build (#44) 2017-12-22 02:48:31 +01:00
Maxime Dor
7fff2448a1 Improve the docker experience
- Only one env variable to configure on first usage
- Auto-generate a default config
- Improve doc
2017-12-16 19:58:11 +01:00
Maxime Dor
6571ff76b1 Take LDAP filter into account when doing 3PID lookups 2017-12-16 19:17:22 +01:00
Maxime Dor
16690a0329 Enforce baseDn for LDAP provider 2017-12-06 20:31:06 +01:00
Maxime Dor
6ac593f0fa Fix typo 2017-12-02 20:13:25 +01:00
Maxime Dor
1581ab9e07 Better logic (cosmetic) for default 3PID notification providers 2017-11-30 02:44:21 +01:00
Maxime Dor
a1adca72e8 Properly select raw notification handler by default 2017-11-26 16:30:42 +01:00
44 changed files with 1387 additions and 330 deletions

View File

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

View File

@@ -18,7 +18,7 @@ It is specifically designed to connect to an Identity store (AD/Samba/LDAP, SQL
and ease the integration of the Matrix ecosystem with an existing infrastructure, or to build a new one using lasting
tools.
The core principle of mxisd is to map between Matrix IDs and 3PIDs (Thrid-party Identifiers) for the Homeserver and its
The core principle of mxisd is to map between Matrix IDs and 3PIDs (Third-party Identifiers) for the Homeserver and its
users. 3PIDs can be anything that identify a user, like:
- Full name
- Email address

View File

@@ -6,6 +6,7 @@
# Matrix config items #
#######################
# Matrix domain, same as the domain configure in your Homeserver configuration.
# (note: in Synapse Homeserver, the Matrix domain may be defined as 'server_name' in configuration file).
#
# This is used to build the various identifiers for identity, auth and directory.
matrix.domain: ''
@@ -17,10 +18,12 @@ matrix.domain: ''
# Absolute path for the Identity Server signing key.
# During testing, /var/tmp/mxisd.key is a possible value
#
# For production, use a stable location like:
# For production, recommended location shall be one of the following:
# - /var/opt/mxisd/sign.key
# - /var/local/mxisd/sign.key
# - /var/lib/mxisd/sign.key
#
# The signing key is auto-generated during execution time if not present.
key.path: ''
@@ -92,5 +95,5 @@ threepid.medium.email.connectors.smtp.login: "matrix-identity@example.org"
# Password for the account
threepid.medium.email.connectors.smtp.password: "ThePassword"
# The e-mail to send as. If empty, will be the same as login
# The e-mail to send as.
threepid.medium.email.identity.from: "matrix-identity@example.org"

View File

@@ -74,10 +74,10 @@ dependencies {
compile 'commons-io:commons-io:2.5'
// Spring Boot - standalone app
compile 'org.springframework.boot:spring-boot-starter-web:1.5.3.RELEASE'
compile 'org.springframework.boot:spring-boot-starter-web:1.5.10.RELEASE'
// Thymeleaf for HTML templates
compile "org.springframework.boot:spring-boot-starter-thymeleaf:1.5.3.RELEASE"
compile "org.springframework.boot:spring-boot-starter-thymeleaf:1.5.10.RELEASE"
// Matrix Java SDK
compile 'io.kamax:matrix-java-sdk:0.0.2'

View File

@@ -1,6 +1,6 @@
# Architecture
## Overview
### Basic setup without integration or federation
### Basic setup without integration or incoming federation
```
Client
|

View File

@@ -1,9 +1,31 @@
# Authentication
Authentication is an enchanced Identity feature of mxisd to ensure coherent and centralized identity management.
- [Description](#description)
- [Overview](#overview)
- [Getting started](#getting-started)
- [Synapse](#synapse)
- [mxisd](#mxisd)
- [Validate](#validate)
- [Next steps](#next-steps)
- [Profile auto-fil](#profile-auto-fill)
- [Advanced Authentication](#advanced-authentication)
- [Requirements](#requirements)
- [Configuration](#configuration)
- [Reverse Proxy](#reverse-proxy)
- [Apache2](#apache2)
- [DNS Overwrite](#dns-overwrite)
- [Backends](#backends)
## Description
Authentication is an enhanced Identity feature of mxisd to ensure coherent and centralized identity management.
It allows to use Identity stores configured in mxisd to authenticate users on your Homeserver.
This feature can also provide the ability to users to login on the Homeserver using their third party identities (3PIDs) provided by an Identity store.
## Overview
An overview of the Authentication process is depicted below:
```
Backends
Client +------+
@@ -11,7 +33,7 @@ It allows to use Identity stores configured in mxisd to authenticate users on yo
| +---------------+ /_matrix/identity | mxisd | | +------+
+-> | Reverse proxy | >------------------+ | | |
+--|------------+ | | | | +--------+
| +-----> Check wiht backends >------+--> | SQL DB |
| +-----> Check with backends >------+--> | SQL DB |
Login request | | | | +--------+
| | | | | |
| +--------------------------+ | +-----|-------------------+ +--> Others
@@ -52,3 +74,98 @@ Auto-filling user profile depends on two conditions:
- The REST auth module is configured for it, which is the case by default
- Your Identity store is configured to provide profile data. See your Identity store [documentation](../backends/) on
how to enable the feature.
## Advanced Authentication
The Authentication feature allows users to login to their Homeserver by using their 3PIDs registered in an available Identity store.
This is performed by intercepting the Homeserver endpoint `/_matrix/client/r0/login` as depicted below:
```
+----------------------------+
| Reverse Proxy |
| |
| | Step 1 +---------------------------+ Step 2
| | | |
Client+---->| /_matrix/client/r0/login +---------------->| | Look up address +---------+
| ^ | | mxisd - Identity server +----------------->| Backend |
| | | | | +---------+
| /_matrix/* +--+ +---------------------+ |
| | | +---------------+-----------+
| | | Step 4 |
| | | | Step 3
+---------------|------------+ |
| | /_matrix/client/r0/login
| +--------------+ |
| | | |
+---------------------->| Homeserver |<----+
| |
+--------------+
```
Steps of user authentication using a 3PID:
1. The intercepted login request is directly sent to mxisd instead of the Homeserver.
2. Enabled backends are queried for a matching user identity in order to modify the request to use the user name.
3. The Homeserver, from which the request was intercepted, is queried using the request at previous step. Its address is resolved using the DNS Overwrite feature to reach its internal address on a non-encrypted port.
4. The response from the Homeserver is sent back to the client, believing it was the HS which directly answered.
### Requirements
- Reverse proxy setup
- Homeserver
- Compatible Identity backends:
- LDAP
- SQL
- REST
### Configuration
#### Reverse Proxy
##### Apache2
The specific configuration to put under the relevant `VirtualHost`:
```
ProxyPass /_matrix/client/r0/login http://localhost:8090/_matrix/client/r0/login
```
`ProxyPreserveHost` or equivalent must be enabled to detect to which Homeserver mxisd should talk to when building results.
Your VirtualHost should now look like this:
```
<VirtualHost *:443>
ServerName example.org
...
ProxyPreserveHost on
ProxyPass /_matrix/client/r0/login http://localhost:8090/_matrix/client/r0/login
ProxyPass /_matrix/identity/ http://localhost:8090/_matrix/identity/
ProxyPass /_matrix/ http://localhost:8008/_matrix/
</VirtualHost>
```
#### DNS Overwrite
Just like you need to configure a reverse proxy to send client requests to mxisd, you also need to configure mxisd with the internal IP of the Homeserver so it can talk to it directly to integrate its directory search.
To do so, put the following configuration in your `application.yaml`:
```
dns.overwrite.homeserver.client:
- name: 'example.org'
value: 'http://localhost:8008'
```
`name` must be the hostname of the URL that clients use when connecting to the Homeserver.
In case the hostname is the same as your Matrix domain, you can use `${matrix.domain}` to auto-populate the `value` using the `matrix.domain` configuration option and avoid duplicating it.
value is the base internal URL of the Homeserver, without any /_matrix/.. or trailing /.
#### Backends
The Backends should be configured as described in the documentation of the [Directory User](directory-users.md) feature.

View File

@@ -4,6 +4,8 @@
- [Requirements](#requirements)
- [Configuration](#configuration)
- [Reverse Proxy](#reverse-proxy)
- [Apache2](#apache2)
- [nginx](#nginx)
- [DNS Overwrite](#dns-overwrite)
- [Backends](#backends)
- [LDAP](#ldap)
@@ -62,16 +64,66 @@ which directly answered the request.
## Configuration
### Reverse Proxy
Apache2 configuration to put under the relevant virtual domain:
#### Apache2
The specific configuration to put under the relevant `VirtualHost`:
```
ProxyPreserveHost on
ProxyPass /_matrix/identity/ http://mxisdInternalIpAddress:8090/_matrix/identity/
ProxyPass /_matrix/client/r0/user_directory/ http://mxisdInternalIpAddress:8090/_matrix/client/r0/user_directory/
ProxyPass /_matrix/ http://HomeserverInternalIpAddress:8008/_matrix/
ProxyPass /_matrix/client/r0/user_directory/ http://0.0.0.0:8090/_matrix/client/r0/user_directory/
```
`ProxyPreserveHost` or equivalent must be enabled to detect to which Homeserver mxisd should talk to when building
results.
Your `VirtualHost` should now look like this:
```
<VirtualHost *:443>
ServerName example.org
...
ProxyPreserveHost on
ProxyPass /_matrix/client/r0/user_directory/ http://localhost:8090/_matrix/client/r0/user_directory/
ProxyPass /_matrix/identity/ http://localhost:8090/_matrix/identity/
ProxyPass /_matrix/ http://localhost:8008/_matrix/
</VirtualHost>
```
#### nginx
The specific configuration to add under your `server` section is:
```
location /_matrix/client/r0/user_directory {
proxy_pass http://0.0.0.0:8090/_matrix/client/r0/user_directory;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
}
```
Your `server` section should now look like this:
```
server {
listen 443 ssl;
server_name example.org;
...
location /_matrix/client/r0/user_directory {
proxy_pass http://localhost:8090/_matrix/client/r0/user_directory;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
}
location /_matrix/identity {
proxy_pass http://localhost:8090/_matrix/identity;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
}
location /_matrix {
proxy_pass http://localhost:8008/_matrix;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
}
}
```
### DNS Overwrite
Just like you need to configure a reverse proxy to send client requests to mxisd, you also need to configure mxisd with
the internal IP of the Homeserver so it can talk to it directly to integrate its directory search.

View File

@@ -1,3 +1,18 @@
To be documented.
# Matrix Identity Service
**WARNING**: This document is incomplete and can be missleading.
Implementation of the [Matrix Identity service API](https://matrix.org/docs/spec/identity_service/unstable.html)
Implementation of the [Unofficial Matrix Identity Service API](https://kamax.io/matrix/api/identity_service/unstable.html).
## Invitation
Resolution can be customized using the following configuration:
`invite.resolution.recursive`
- Default value: `true`
- Description: Control if the pending invite resolution should be done recursively or not.
**DANGER ZONE:** This setting has the potential to create "an isolated island", which can have unexpected side effects
and break invites in rooms. This will most likely not have the effect you think it does. Only change the value if you
understand the consequences.
`invite.resolution.timer`
- Default value: `1`
- Description: How often, in minutes, mxisd should try to resolve pending invites.

View File

@@ -37,6 +37,8 @@ Install via:
See the [Latest release](https://github.com/kamax-io/mxisd/releases/latest) for links to each.
## Configure
**NOTE**: please view the install instruction for your platform, as this step might be optional/handled for you.
Create/edit a minimal configuration (see installer doc for the location):
```
matrix.domain: 'MyMatrixDomain.org'
@@ -54,14 +56,14 @@ Complete configuration guide is available [here](configure.md).
For an overview of a typical mxisd infrastructure, see the [dedicated document](architecture.md)
### Reverse proxy
#### Apache2
In the VirtualHost handling the domain with SSL, add the following line and replace `0.0.0.0` by the internal IP/hostname
pointing to mxisd.
In the `VirtualHost` section handling the domain with SSL, add the following and replace `0.0.0.0` by the internal
hostname/IP pointing to mxisd.
**This line MUST be present before the one for the homeserver!**
```
ProxyPass /_matrix/identity/ http://0.0.0.0:8090/_matrix/identity/
```
Typical VirtualHost configuration would be:
Typical configuration would look like:
```
<VirtualHost *:443>
ServerName example.org
@@ -69,11 +71,43 @@ Typical VirtualHost configuration would be:
...
ProxyPreserveHost on
ProxyPass /_matrix/identity/ http://10.1.2.3:8090/_matrix/identity/
ProxyPass /_matrix/ http://10.1.2.3:8008/_matrix/
ProxyPass /_matrix/identity/ http://localhost:8090/_matrix/identity/
ProxyPass /_matrix/ http://localhost:8008/_matrix/
</VirtualHost>
```
#### nginx
In the `server` section handling the domain with SSL, add the following and replace `0.0.0.0` with the internal
hostname/IP pointing to mxisd.
**This line MUST be present before the one for the homeserver!**
```
location /_matrix/identity {
proxy_pass http://0.0.0.0:8090/_matrix/identity;
}
```
Typical configuration would look like:
```
server {
listen 443 ssl;
server_name example.org;
...
location /_matrix/identity {
proxy_pass http://localhost:8090/_matrix/identity;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
}
location /_matrix {
proxy_pass http://localhost:8008/_matrix;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
}
}
```
### Synapse
Add your mxisd domain into the `homeserver.yaml` at `trusted_third_party_id_servers` and restart synapse.
In a typical configuration, you would end up with something similair to:

View File

@@ -5,10 +5,18 @@ Pull the latest stable image:
docker pull kamax/mxisd
```
## Configure
On first run, simply using `MATRIX_DOMAIN` as an environment variable will create a default config for you.
You can also provide a configuration file named `mxisd.yaml` in the volume mapped to `/etc/mxisd` before starting your
container.
## Run
Run it (adapt volume paths to your host):
Use the following command after adapting to your needs:
- The `MATRIX_DOMAIN` environment variable to yours
- The volumes host paths
```
docker run --rm -v /data/mxisd/etc:/etc/mxisd -v /data/mxisd/var:/var/mxisd -p 8090:8090 -t kamax/mxisd
docker run --rm -e MATRIX_DOMAIN=example.org -v /data/mxisd/etc:/etc/mxisd -v /data/mxisd/var:/var/mxisd -p 8090:8090 -t kamax/mxisd
```
For more info, including the list of possible tags, see [the public repository](https://hub.docker.com/r/kamax/mxisd/)

View File

@@ -276,14 +276,14 @@ session:
**IMPORTANT**: When using local-only mode, you will also need to link mxisd to synapse if you want user searches and invites to work.
To do so, add/edit the following configuration keys:
```
sql:
synapseSql:
enabled: true
type: 'postgresql'
connection: ''
type: 'SET TO PROPER VALUE'
connection: 'SET TO PROPER VALUE'
```
- `sql.enabled` set to `true` to activate the SQL backend.
- `sql.type` can be set to `sqlite` or `postgresql`, depending on your synapse setup.
- `sql.connection` use a JDBC format which is appened after the `jdbc:type:` connection URI.
- `synapseSql.enabled` set to `true` to activate the SQL backend.
- `synapseSql.type` can be set to `sqlite` or `postgresql`, depending on your synapse setup.
- `synapseSql.connection` use a JDBC format which is appened after the `jdbc:type:` connection URI.
Example values for each type:
- `sqlite`: `/path/to/homeserver.db`
- `postgresql`: `//localhost/database?user=synapse&password=synapse`

View File

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

View File

@@ -29,7 +29,8 @@ import io.kamax.mxisd.UserIdType;
import io.kamax.mxisd.auth.provider.AuthenticatorProvider;
import io.kamax.mxisd.auth.provider.BackendAuthResult;
import io.kamax.mxisd.config.MatrixConfig;
import io.kamax.mxisd.config.ldap.LdapConfig;
import io.kamax.mxisd.config.ldap.generic.GenericLdapConfig;
import io.kamax.mxisd.util.GsonUtil;
import org.apache.commons.lang.StringUtils;
import org.apache.directory.api.ldap.model.cursor.CursorException;
import org.apache.directory.api.ldap.model.cursor.CursorLdapReferralException;
@@ -51,14 +52,14 @@ import java.util.Optional;
import java.util.Set;
@Component
public class LdapAuthProvider extends LdapGenericBackend implements AuthenticatorProvider {
public class LdapAuthProvider extends LdapBackend implements AuthenticatorProvider {
private Logger log = LoggerFactory.getLogger(LdapAuthProvider.class);
private PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
@Autowired
public LdapAuthProvider(LdapConfig cfg, MatrixConfig mxCfg) {
public LdapAuthProvider(GenericLdapConfig cfg, MatrixConfig mxCfg) {
super(cfg, mxCfg);
}
@@ -91,7 +92,7 @@ public class LdapAuthProvider extends LdapGenericBackend implements Authenticato
bind(conn);
String uidType = getAt().getUid().getType();
String userFilterValue = StringUtils.equals(LdapGenericBackend.UID, uidType) ? mxid.getLocalPart() : mxid.getId();
String userFilterValue = StringUtils.equals(LdapBackend.UID, uidType) ? mxid.getLocalPart() : mxid.getId();
if (StringUtils.isBlank(userFilterValue)) {
log.warn("Username is empty, failing auth");
return BackendAuthResult.failure();
@@ -107,6 +108,10 @@ public class LdapAuthProvider extends LdapGenericBackend implements Authenticato
String[] attArray = new String[attributes.size()];
attributes.toArray(attArray);
log.debug("Base DN: {}", getBaseDn());
log.debug("Query: {}", userFilter);
log.debug("Attributes: {}", GsonUtil.build().toJson(attArray));
try (EntryCursor cursor = conn.search(getBaseDn(), userFilter, SearchScope.SUBTREE, attArray)) {
while (cursor.next()) {
Entry entry = cursor.get();

View File

@@ -21,7 +21,6 @@
package io.kamax.mxisd.backend.ldap;
import io.kamax.mxisd.config.MatrixConfig;
import io.kamax.mxisd.config.ldap.LdapAttributeConfig;
import io.kamax.mxisd.config.ldap.LdapConfig;
import org.apache.commons.lang.StringUtils;
import org.apache.directory.api.ldap.model.entry.Attribute;
@@ -40,17 +39,17 @@ import java.util.Arrays;
import java.util.List;
import java.util.Optional;
public abstract class LdapGenericBackend {
public abstract class LdapBackend {
public static final String UID = "uid";
public static final String MATRIX_ID = "mxid";
private Logger log = LoggerFactory.getLogger(LdapGenericBackend.class);
private Logger log = LoggerFactory.getLogger(LdapBackend.class);
private LdapConfig cfg;
private MatrixConfig mxCfg;
public LdapGenericBackend(LdapConfig cfg, MatrixConfig mxCfg) {
public LdapBackend(LdapConfig cfg, MatrixConfig mxCfg) {
this.cfg = cfg;
this.mxCfg = mxCfg;
}
@@ -60,10 +59,10 @@ public abstract class LdapGenericBackend {
}
protected String getBaseDn() {
return cfg.getConn().getBaseDn();
return cfg.getConnection().getBaseDn();
}
protected LdapAttributeConfig getAt() {
protected LdapConfig.Attribute getAt() {
return cfg.getAttribute();
}
@@ -72,14 +71,14 @@ public abstract class LdapGenericBackend {
}
protected synchronized LdapConnection getConn() throws LdapException {
return new LdapNetworkConnection(cfg.getConn().getHost(), cfg.getConn().getPort(), cfg.getConn().isTls());
return new LdapNetworkConnection(cfg.getConnection().getHost(), cfg.getConnection().getPort(), cfg.getConnection().isTls());
}
protected void bind(LdapConnection conn) throws LdapException {
if (StringUtils.isBlank(cfg.getConn().getBindDn()) && StringUtils.isBlank(cfg.getConn().getBindPassword())) {
if (StringUtils.isBlank(cfg.getConnection().getBindDn()) && StringUtils.isBlank(cfg.getConnection().getBindPassword())) {
conn.anonymousBind();
} else {
conn.bind(cfg.getConn().getBindDn(), cfg.getConn().getBindPassword());
conn.bind(cfg.getConnection().getBindDn(), cfg.getConnection().getBindPassword());
}
}

View File

@@ -21,11 +21,12 @@
package io.kamax.mxisd.backend.ldap;
import io.kamax.mxisd.config.MatrixConfig;
import io.kamax.mxisd.config.ldap.LdapAttributeConfig;
import io.kamax.mxisd.config.ldap.LdapConfig;
import io.kamax.mxisd.config.ldap.generic.GenericLdapConfig;
import io.kamax.mxisd.controller.directory.v1.io.UserDirectorySearchResult;
import io.kamax.mxisd.directory.IDirectoryProvider;
import io.kamax.mxisd.exception.InternalServerError;
import io.kamax.mxisd.util.GsonUtil;
import org.apache.directory.api.ldap.model.cursor.CursorException;
import org.apache.directory.api.ldap.model.cursor.CursorLdapReferralException;
import org.apache.directory.api.ldap.model.cursor.EntryCursor;
@@ -43,12 +44,12 @@ import java.util.ArrayList;
import java.util.List;
@Component
public class LdapDirectoryProvider extends LdapGenericBackend implements IDirectoryProvider {
public class LdapDirectoryProvider extends LdapBackend implements IDirectoryProvider {
private Logger log = LoggerFactory.getLogger(LdapDirectoryProvider.class);
@Autowired
public LdapDirectoryProvider(LdapConfig cfg, MatrixConfig mxCfg) {
public LdapDirectoryProvider(GenericLdapConfig cfg, MatrixConfig mxCfg) {
super(cfg, mxCfg);
}
@@ -64,15 +65,18 @@ public class LdapDirectoryProvider extends LdapGenericBackend implements IDirect
try (LdapConnection conn = getConn()) {
bind(conn);
LdapAttributeConfig atCfg = getCfg().getAttribute();
LdapConfig.Attribute atCfg = getCfg().getAttribute();
attributes = new ArrayList<>(attributes);
attributes.add(getUidAtt());
String[] attArray = new String[attributes.size()];
attributes.toArray(attArray);
String searchQuery = buildOrQueryWithFilter(getCfg().getDirectory().getFilter(), "*" + query + "*", attArray);
log.debug("Base DN: {}", getBaseDn());
log.debug("Query: {}", searchQuery);
log.debug("Attributes: {}", GsonUtil.build().toJson(attArray));
try (EntryCursor cursor = conn.search(getBaseDn(), searchQuery, SearchScope.SUBTREE, attArray)) {
while (cursor.next()) {
Entry entry = cursor.get();

View File

@@ -21,12 +21,13 @@
package io.kamax.mxisd.backend.ldap;
import io.kamax.mxisd.config.MatrixConfig;
import io.kamax.mxisd.config.ldap.LdapConfig;
import io.kamax.mxisd.config.ldap.generic.GenericLdapConfig;
import io.kamax.mxisd.exception.InternalServerError;
import io.kamax.mxisd.lookup.SingleLookupReply;
import io.kamax.mxisd.lookup.SingleLookupRequest;
import io.kamax.mxisd.lookup.ThreePidMapping;
import io.kamax.mxisd.lookup.provider.IThreePidProvider;
import io.kamax.mxisd.util.GsonUtil;
import org.apache.directory.api.ldap.model.cursor.CursorException;
import org.apache.directory.api.ldap.model.cursor.CursorLdapReferralException;
import org.apache.directory.api.ldap.model.cursor.EntryCursor;
@@ -44,11 +45,11 @@ import java.util.List;
import java.util.Optional;
@Component
public class LdapThreePidProvider extends LdapGenericBackend implements IThreePidProvider {
public class LdapThreePidProvider extends LdapBackend implements IThreePidProvider {
private Logger log = LoggerFactory.getLogger(LdapThreePidProvider.class);
public LdapThreePidProvider(LdapConfig cfg, MatrixConfig mxCfg) {
public LdapThreePidProvider(GenericLdapConfig cfg, MatrixConfig mxCfg) {
super(cfg, mxCfg);
}
@@ -68,13 +69,20 @@ public class LdapThreePidProvider extends LdapGenericBackend implements IThreePi
}
private Optional<String> lookup(LdapConnection conn, String medium, String value) {
Optional<String> queryOpt = getCfg().getIdentity().getQuery(medium);
if (!queryOpt.isPresent()) {
Optional<String> tPidQueryOpt = getCfg().getIdentity().getQuery(medium);
if (!tPidQueryOpt.isPresent()) {
log.warn("{} is not a configured 3PID type for LDAP lookup", medium);
return Optional.empty();
}
String searchQuery = queryOpt.get().replaceAll(getCfg().getIdentity().getToken(), value);
// we merge 3PID specific query with global/specific filter, if one exists.
String tPidQuery = tPidQueryOpt.get().replaceAll(getCfg().getIdentity().getToken(), value);
String searchQuery = buildWithFilter(tPidQuery, getCfg().getIdentity().getFilter());
log.debug("Base DN: {}", getBaseDn());
log.debug("Query: {}", searchQuery);
log.debug("Attributes: {}", GsonUtil.build().toJson(getUidAtt()));
try (EntryCursor cursor = conn.search(getBaseDn(), searchQuery, SearchScope.SUBTREE, getUidAtt())) {
while (cursor.next()) {
Entry entry = cursor.get();

View File

@@ -0,0 +1,41 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2018 Kamax Sàrl
*
* https://www.kamax.io/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.mxisd.backend.ldap.netiq;
import io.kamax.mxisd.backend.ldap.LdapAuthProvider;
import io.kamax.mxisd.config.MatrixConfig;
import io.kamax.mxisd.config.ldap.netiq.NetIqLdapConfig;
import org.springframework.stereotype.Component;
@Component
public class NetIqLdapAuthProvider extends LdapAuthProvider {
public NetIqLdapAuthProvider(NetIqLdapConfig cfg, MatrixConfig mxCfg) {
super(cfg, mxCfg);
}
// FIXME this is duplicated in the other NetIQ classes, due to the Matrix ID generation code that was not abstracted
@Override
public String buildMatrixIdFromUid(String uid) {
return super.buildMatrixIdFromUid(uid).toLowerCase();
}
}

View File

@@ -0,0 +1,41 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2018 Kamax Sàrl
*
* https://www.kamax.io/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.mxisd.backend.ldap.netiq;
import io.kamax.mxisd.backend.ldap.LdapDirectoryProvider;
import io.kamax.mxisd.config.MatrixConfig;
import io.kamax.mxisd.config.ldap.netiq.NetIqLdapConfig;
import org.springframework.stereotype.Component;
@Component
public class NetIqLdapDirectoryProvider extends LdapDirectoryProvider {
public NetIqLdapDirectoryProvider(NetIqLdapConfig cfg, MatrixConfig mxCfg) {
super(cfg, mxCfg);
}
// FIXME this is duplicated in the other NetIQ classes, due to the Matrix ID generation code that was not abstracted
@Override
public String buildMatrixIdFromUid(String uid) {
return super.buildMatrixIdFromUid(uid).toLowerCase();
}
}

View File

@@ -0,0 +1,41 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2018 Kamax Sàrl
*
* https://www.kamax.io/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.mxisd.backend.ldap.netiq;
import io.kamax.mxisd.backend.ldap.LdapThreePidProvider;
import io.kamax.mxisd.config.MatrixConfig;
import io.kamax.mxisd.config.ldap.netiq.NetIqLdapConfig;
import org.springframework.stereotype.Component;
@Component
public class NetIqLdapThreePidProvider extends LdapThreePidProvider {
public NetIqLdapThreePidProvider(NetIqLdapConfig cfg, MatrixConfig mxCfg) {
super(cfg, mxCfg);
}
// FIXME this is duplicated in the other NetIQ classes, due to the Matrix ID generation code that was not abstracted
@Override
public String buildMatrixIdFromUid(String uid) {
return super.buildMatrixIdFromUid(uid).toLowerCase();
}
}

View File

@@ -0,0 +1,111 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2018 Maxime Dor
*
* https://www.kamax.io/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.mxisd.backend.memory;
import io.kamax.matrix.MatrixID;
import io.kamax.matrix.ThreePid;
import io.kamax.matrix._MatrixID;
import io.kamax.mxisd.UserIdType;
import io.kamax.mxisd.auth.provider.AuthenticatorProvider;
import io.kamax.mxisd.auth.provider.BackendAuthResult;
import io.kamax.mxisd.config.MatrixConfig;
import io.kamax.mxisd.config.memory.MemoryIdentityConfig;
import io.kamax.mxisd.config.memory.MemoryStoreConfig;
import io.kamax.mxisd.config.memory.MemoryThreePid;
import io.kamax.mxisd.lookup.SingleLookupReply;
import io.kamax.mxisd.lookup.SingleLookupRequest;
import io.kamax.mxisd.lookup.ThreePidMapping;
import io.kamax.mxisd.lookup.provider.IThreePidProvider;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
@Component
public class MemoryIdentityStore implements AuthenticatorProvider, IThreePidProvider {
private final Logger logger = LoggerFactory.getLogger(MemoryIdentityStore.class);
private final MatrixConfig mxCfg;
private final MemoryStoreConfig cfg;
@Autowired
public MemoryIdentityStore(MatrixConfig mxCfg, MemoryStoreConfig cfg) {
this.mxCfg = mxCfg;
this.cfg = cfg;
}
public Optional<MemoryIdentityConfig> findByUsername(String username) {
return cfg.getIdentities().stream().filter(id -> StringUtils.equals(id.getUsername(), username)).findFirst();
}
@Override
public boolean isEnabled() {
return cfg.isEnabled();
}
@Override
public boolean isLocal() {
return true;
}
@Override
public int getPriority() {
return Integer.MAX_VALUE;
}
@Override
public Optional<SingleLookupReply> find(SingleLookupRequest request) {
logger.info("Performing lookup {} of type {}", request.getThreePid(), request.getType());
ThreePid req = new ThreePid(request.getType(), request.getThreePid());
for (MemoryIdentityConfig id : cfg.getIdentities()) {
for (MemoryThreePid threepid : id.getThreepids()) {
if (req.equals(new ThreePid(threepid.getMedium(), threepid.getAddress()))) {
return Optional.of(new SingleLookupReply(request, new MatrixID(id.getUsername(), mxCfg.getDomain())));
}
}
}
return Optional.empty();
}
@Override
public List<ThreePidMapping> populate(List<ThreePidMapping> mappings) {
return Collections.emptyList();
}
@Override
public BackendAuthResult authenticate(_MatrixID mxid, String password) {
return findByUsername(mxid.getLocalPart()).map(id -> {
if (!StringUtils.equals(id.getUsername(), mxid.getLocalPart())) {
return BackendAuthResult.failure();
} else {
return BackendAuthResult.success(mxid.getId(), UserIdType.MatrixID, "");
}
}).orElseGet(BackendAuthResult::failure);
}
}

View File

@@ -31,10 +31,12 @@ import io.kamax.mxisd.util.RestClientUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
@Component
public class RestDirectoryProvider extends RestProvider implements IDirectoryProvider {
private MatrixConfig mxCfg;

View File

@@ -0,0 +1,76 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2018 Kamax Sàrl
*
* https://www.kamax.io/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.mxisd.config;
import io.kamax.mxisd.util.GsonUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PostConstruct;
@Configuration
@ConfigurationProperties("invite")
public class InvitationConfig {
private final Logger log = LoggerFactory.getLogger(InvitationConfig.class);
public static class Resolution {
private boolean recursive;
private long timer;
public boolean isRecursive() {
return recursive;
}
public void setRecursive(boolean recursive) {
this.recursive = recursive;
}
public long getTimer() {
return timer;
}
public void setTimer(long timer) {
this.timer = timer;
}
}
private Resolution resolution;
public Resolution getResolution() {
return resolution;
}
public void setResolution(Resolution resolution) {
this.resolution = resolution;
}
@PostConstruct
public void build() {
log.info("--- Invite config ---");
log.info("Resolution: {}", GsonUtil.build().toJson(resolution));
}
}

View File

@@ -22,28 +22,148 @@ package io.kamax.mxisd.config.ldap;
import com.google.gson.Gson;
import io.kamax.matrix.ThreePidMedium;
import io.kamax.mxisd.backend.ldap.LdapGenericBackend;
import io.kamax.mxisd.backend.ldap.LdapBackend;
import io.kamax.mxisd.exception.ConfigurationException;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
import java.util.*;
@Configuration
@ConfigurationProperties(prefix = "ldap")
public class LdapConfig {
private Logger log = LoggerFactory.getLogger(LdapConfig.class);
private static Gson gson = new Gson();
public static class UID {
private boolean enabled;
private String filter;
private String type;
private String value;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
public static class Attribute {
private UID uid;
private String name;
private Map<String, List<String>> threepid = new HashMap<>();
public UID getUid() {
return uid;
}
public void setUid(UID uid) {
this.uid = uid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Map<String, List<String>> getThreepid() {
return threepid;
}
public void setThreepid(Map<String, List<String>> threepid) {
this.threepid = threepid;
}
}
public static class Auth {
private String filter;
public String getFilter() {
return filter;
}
public void setFilter(String filter) {
this.filter = filter;
}
}
public static class Connection {
private boolean tls;
private String host;
private int port;
private String bindDn;
private String bindPassword;
private String baseDn;
public boolean isTls() {
return tls;
}
public void setTls(boolean tls) {
this.tls = tls;
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public String getBindDn() {
return bindDn;
}
public void setBindDn(String bindDn) {
this.bindDn = bindDn;
}
public String getBindPassword() {
return bindPassword;
}
public void setBindPassword(String bindPassword) {
this.bindPassword = bindPassword;
}
public String getBaseDn() {
return baseDn;
}
public void setBaseDn(String baseDn) {
this.baseDn = baseDn;
}
}
public static class Directory {
@@ -82,12 +202,54 @@ public class LdapConfig {
}
@Autowired
private LdapConnectionConfig conn;
private LdapAttributeConfig attribute;
private LdapAuthConfig auth;
public static class Identity {
private String filter;
private String token;
private Map<String, String> medium = new HashMap<>();
public String getFilter() {
return filter;
}
public void setFilter(String filter) {
this.filter = filter;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public Map<String, String> getMedium() {
return medium;
}
public Optional<String> getQuery(String key) {
return Optional.ofNullable(medium.get(key));
}
public void setMedium(Map<String, String> medium) {
this.medium = medium;
}
}
private Logger log = LoggerFactory.getLogger(LdapConfig.class);
private static Gson gson = new Gson();
private boolean enabled;
private String filter;
private Connection connection;
private Attribute attribute;
private Auth auth;
private Directory directory;
private LdapIdentityConfig identity;
private Identity identity;
public boolean isEnabled() {
return enabled;
@@ -105,27 +267,27 @@ public class LdapConfig {
this.filter = filter;
}
public LdapConnectionConfig getConn() {
return conn;
public Connection getConnection() {
return connection;
}
public void setConn(LdapConnectionConfig conn) {
this.conn = conn;
public void setConnection(Connection conn) {
this.connection = conn;
}
public LdapAttributeConfig getAttribute() {
public Attribute getAttribute() {
return attribute;
}
public void setAttribute(LdapAttributeConfig attribute) {
public void setAttribute(Attribute attribute) {
this.attribute = attribute;
}
public LdapAuthConfig getAuth() {
public Auth getAuth() {
return auth;
}
public void setAuth(LdapAuthConfig auth) {
public void setAuth(Auth auth) {
this.auth = auth;
}
@@ -137,11 +299,11 @@ public class LdapConfig {
this.directory = directory;
}
public LdapIdentityConfig getIdentity() {
public Identity getIdentity() {
return identity;
}
public void setIdentity(LdapIdentityConfig identity) {
public void setIdentity(Identity identity) {
this.identity = identity;
}
@@ -154,25 +316,28 @@ public class LdapConfig {
return;
}
if (StringUtils.isBlank(conn.getHost())) {
if (StringUtils.isBlank(connection.getHost())) {
throw new IllegalStateException("LDAP Host must be configured!");
}
if (conn.getPort() < 1 || conn.getPort() > 65535) {
if (connection.getPort() < 1 || connection.getPort() > 65535) {
throw new IllegalStateException("LDAP port is not valid");
}
if (StringUtils.isBlank(connection.getBaseDn())) {
throw new ConfigurationException("ldap.connection.baseDn");
}
if (StringUtils.isBlank(attribute.getUid().getType())) {
throw new IllegalStateException("Attribute UID Type cannot be empty");
}
if (StringUtils.isBlank(attribute.getUid().getValue())) {
throw new IllegalStateException("Attribute UID value cannot be empty");
}
String uidType = attribute.getUid().getType();
if (!StringUtils.equals(LdapGenericBackend.UID, uidType) && !StringUtils.equals(LdapGenericBackend.MATRIX_ID, uidType)) {
if (!StringUtils.equals(LdapBackend.UID, uidType) && !StringUtils.equals(LdapBackend.MATRIX_ID, uidType)) {
throw new IllegalArgumentException("Unsupported LDAP UID type: " + uidType);
}
@@ -184,9 +349,9 @@ public class LdapConfig {
attribute.getThreepid().forEach((k, v) -> {
if (StringUtils.isBlank(identity.getMedium().get(k))) {
if (ThreePidMedium.PhoneNumber.is(k)) {
identity.getMedium().put(k, LdapGenericBackend.buildOrQuery("+" + getIdentity().getToken(), v));
identity.getMedium().put(k, LdapBackend.buildOrQuery("+" + getIdentity().getToken(), v));
} else {
identity.getMedium().put(k, LdapGenericBackend.buildOrQuery(getIdentity().getToken(), v));
identity.getMedium().put(k, LdapBackend.buildOrQuery(getIdentity().getToken(), v));
}
}
});
@@ -195,10 +360,10 @@ public class LdapConfig {
getDirectory().setFilter(StringUtils.defaultIfBlank(getDirectory().getFilter(), getFilter()));
getIdentity().setFilter(StringUtils.defaultIfBlank(getIdentity().getFilter(), getFilter()));
log.info("Host: {}", conn.getHost());
log.info("Port: {}", conn.getPort());
log.info("Bind DN: {}", conn.getBindDn());
log.info("Base DN: {}", conn.getBaseDn());
log.info("Host: {}", connection.getHost());
log.info("Port: {}", connection.getPort());
log.info("Bind DN: {}", connection.getBindDn());
log.info("Base DN: {}", connection.getBaseDn());
log.info("Attribute: {}", gson.toJson(attribute));
log.info("Auth: {}", gson.toJson(auth));

View File

@@ -1,85 +0,0 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2017 Maxime Dor
*
* https://max.kamax.io/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.mxisd.config.ldap;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConfigurationProperties(prefix = "ldap.connection")
public class LdapConnectionConfig {
private boolean tls;
private String host;
private int port;
private String bindDn;
private String bindPassword;
private String baseDn;
public boolean isTls() {
return tls;
}
public void setTls(boolean tls) {
this.tls = tls;
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public String getBindDn() {
return bindDn;
}
public void setBindDn(String bindDn) {
this.bindDn = bindDn;
}
public String getBindPassword() {
return bindPassword;
}
public void setBindPassword(String bindPassword) {
this.bindPassword = bindPassword;
}
public String getBaseDn() {
return baseDn;
}
public void setBaseDn(String baseDn) {
this.baseDn = baseDn;
}
}

View File

@@ -1,66 +0,0 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2017 Maxime Dor
*
* https://max.kamax.io/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.mxisd.config.ldap;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
@Configuration
@ConfigurationProperties(prefix = "ldap.identity")
public class LdapIdentityConfig {
private String filter;
private String token;
private Map<String, String> medium = new HashMap<>();
public String getFilter() {
return filter;
}
public void setFilter(String filter) {
this.filter = filter;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public Map<String, String> getMedium() {
return medium;
}
public Optional<String> getQuery(String key) {
return Optional.ofNullable(medium.get(key));
}
public void setMedium(Map<String, String> medium) {
this.medium = medium;
}
}

View File

@@ -1,8 +1,8 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2017 Maxime Dor
* Copyright (C) 2018 Kamax Sàrl
*
* https://max.kamax.io/
* 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
@@ -18,23 +18,16 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.mxisd.config.ldap;
package io.kamax.mxisd.config.ldap.generic;
import io.kamax.mxisd.config.ldap.LdapConfig;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
@Configuration
@ConfigurationProperties(prefix = "ldap.auth")
public class LdapAuthConfig {
private String filter;
public String getFilter() {
return filter;
}
public void setFilter(String filter) {
this.filter = filter;
}
@ConfigurationProperties(prefix = "ldap")
@Primary
public class GenericLdapConfig extends LdapConfig {
}

View File

@@ -1,8 +1,8 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2017 Maxime Dor
* Copyright (C) 2018 Kamax Sàrl
*
* https://max.kamax.io/
* 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
@@ -18,32 +18,14 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.mxisd.config.ldap;
package io.kamax.mxisd.config.ldap.netiq;
import io.kamax.mxisd.config.ldap.generic.GenericLdapConfig;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConfigurationProperties(prefix = "ldap.attribute.uid")
public class LdapAttributeUidConfig {
private String type;
private String value;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
@ConfigurationProperties(prefix = "netiq")
public class NetIqLdapConfig extends GenericLdapConfig {
}

View File

@@ -0,0 +1,59 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2018 Maxime Dor
*
* https://www.kamax.io/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.mxisd.config.memory;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
@Component
public class MemoryIdentityConfig {
private String username;
private String password;
private List<MemoryThreePid> threepids = new ArrayList<>();
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public List<MemoryThreePid> getThreepids() {
return threepids;
}
public void setThreepids(List<MemoryThreePid> threepids) {
this.threepids = threepids;
}
}

View File

@@ -1,8 +1,8 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2017 Maxime Dor
* Copyright (C) 2018 Maxime Dor
*
* https://max.kamax.io/
* 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
@@ -18,45 +18,34 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.mxisd.config.ldap;
package io.kamax.mxisd.config.memory;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Configuration
@ConfigurationProperties(prefix = "ldap.attribute")
public class LdapAttributeConfig {
@ConfigurationProperties("memory")
public class MemoryStoreConfig {
private LdapAttributeUidConfig uid;
private String name;
private Map<String, List<String>> threepid = new HashMap<>();
private boolean enabled;
private List<MemoryIdentityConfig> identities;
public LdapAttributeUidConfig getUid() {
return uid;
public boolean isEnabled() {
return enabled;
}
public void setUid(LdapAttributeUidConfig uid) {
this.uid = uid;
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public String getName() {
return name;
public List<MemoryIdentityConfig> getIdentities() {
return identities;
}
public void setName(String name) {
this.name = name;
}
public Map<String, List<String>> getThreepid() {
return threepid;
}
public void setThreepid(Map<String, List<String>> threepid) {
this.threepid = threepid;
public void setIdentities(List<MemoryIdentityConfig> identities) {
this.identities = identities;
}
}

View File

@@ -0,0 +1,47 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2018 Maxime Dor
*
* https://www.kamax.io/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.mxisd.config.memory;
import org.springframework.stereotype.Component;
@Component
public class MemoryThreePid {
private String medium;
private String address;
public String getMedium() {
return medium;
}
public void setMedium(String medium) {
this.medium = medium;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}

View File

@@ -54,6 +54,16 @@ public class DefaultExceptionHandler {
return gson.toJson(obj);
}
@ExceptionHandler(RemoteLoginException.class)
public String handle(HttpServletRequest request, HttpServletResponse response, RemoteLoginException e) {
if (e.getErrorBodyMsgResp() != null) {
response.setStatus(e.getStatus());
log.info("Request {} {} - Error {}: {}", request.getMethod(), request.getRequestURL(), e.getErrorCode(), e.getError());
return gson.toJson(e.getErrorBodyMsgResp());
}
return handleGeneric(request, response, e);
}
@ExceptionHandler(InternalServerError.class)
public String handle(HttpServletRequest request, HttpServletResponse response, InternalServerError e) {
if (StringUtils.isNotBlank(e.getInternalReason())) {

View File

@@ -0,0 +1,38 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2018 Maxime Dor
*
* 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.controller;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@CrossOrigin
@RequestMapping(produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public class PingController {
@RequestMapping(value = "/_matrix/identity/api/v1")
public String ping() {
return "{}";
}
}

View File

@@ -20,15 +20,27 @@
package io.kamax.mxisd.controller.auth.v1;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.*;
import com.google.i18n.phonenumbers.NumberParseException;
import com.google.i18n.phonenumbers.PhoneNumberUtil;
import com.google.i18n.phonenumbers.Phonenumber;
import io.kamax.mxisd.auth.AuthManager;
import io.kamax.mxisd.auth.UserAuthResult;
import io.kamax.mxisd.controller.auth.v1.io.CredentialsValidationResponse;
import io.kamax.mxisd.dns.ClientDnsOverwrite;
import io.kamax.mxisd.exception.JsonMemberNotFoundException;
import io.kamax.mxisd.exception.RemoteLoginException;
import io.kamax.mxisd.lookup.strategy.LookupStrategy;
import io.kamax.mxisd.util.GsonParser;
import io.kamax.mxisd.util.GsonUtil;
import io.kamax.mxisd.util.RestClientUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -39,13 +51,18 @@ import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URI;
@RestController
@CrossOrigin
@RequestMapping(produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public class AuthController {
// TODO export into SDK
private static final String logV1Url = "/_matrix/client/r0/login";
private Logger log = LoggerFactory.getLogger(AuthController.class);
private Gson gson = GsonUtil.build();
@@ -54,6 +71,23 @@ public class AuthController {
@Autowired
private AuthManager mgr;
@Autowired
private LookupStrategy strategy;
@Autowired
private ClientDnsOverwrite dns;
@Autowired
private CloseableHttpClient client;
private String resolveProxyUrl(HttpServletRequest req) {
URI target = URI.create(req.getRequestURL().toString());
URIBuilder builder = dns.transform(target);
String urlToLogin = builder.toString();
log.info("Proxy resolution: {} to {}", target.toString(), urlToLogin);
return urlToLogin;
}
@RequestMapping(value = "/_matrix-internal/identity/v1/check_credentials", method = RequestMethod.POST)
public String checkCredentials(HttpServletRequest req) {
try {
@@ -84,4 +118,112 @@ public class AuthController {
}
}
@RequestMapping(value = logV1Url, method = RequestMethod.GET)
public String getLogin(HttpServletRequest req, HttpServletResponse res) {
try (CloseableHttpResponse hsResponse = client.execute(new HttpGet(resolveProxyUrl(req)))) {
res.setStatus(hsResponse.getStatusLine().getStatusCode());
return EntityUtils.toString(hsResponse.getEntity());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@RequestMapping(value = logV1Url, method = RequestMethod.POST)
public String login(HttpServletRequest req) {
try {
JsonObject reqJsonObject = parser.parse(req.getInputStream());
// find 3PID in main object
GsonUtil.findPrimitive(reqJsonObject, "medium").ifPresent(medium -> {
GsonUtil.findPrimitive(reqJsonObject, "address").ifPresent(address -> {
log.info("Login request with medium '{}' and address '{}'", medium.getAsString(), address.getAsString());
strategy.findLocal(medium.getAsString(), address.getAsString()).ifPresent(lookupDataOpt -> {
reqJsonObject.addProperty("user", lookupDataOpt.getMxid().getLocalPart());
reqJsonObject.remove("medium");
reqJsonObject.remove("address");
});
});
});
// find 3PID in 'identifier' object
GsonUtil.findObj(reqJsonObject, "identifier").ifPresent(identifier -> {
GsonUtil.findPrimitive(identifier, "type").ifPresent(type -> {
if (StringUtils.equals(type.getAsString(), "m.id.thirdparty")) {
GsonUtil.findPrimitive(identifier, "medium").ifPresent(medium -> {
GsonUtil.findPrimitive(identifier, "address").ifPresent(address -> {
log.info("Login request with medium '{}' and address '{}'", medium.getAsString(), address.getAsString());
strategy.findLocal(medium.getAsString(), address.getAsString()).ifPresent(lookupDataOpt -> {
identifier.addProperty("type", "m.id.user");
identifier.addProperty("user", lookupDataOpt.getMxid().getLocalPart());
identifier.remove("medium");
identifier.remove("address");
});
});
});
}
if (StringUtils.equals(type.getAsString(), "m.id.phone")) {
GsonUtil.findPrimitive(identifier, "number").ifPresent(number -> {
GsonUtil.findPrimitive(identifier, "country").ifPresent(country -> {
log.info("Login request with phone '{}'-'{}'", country.getAsString(), number.getAsString());
try {
PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
Phonenumber.PhoneNumber phoneNumber = phoneUtil.parse(number.getAsString(), country.getAsString());
String canon_phoneNumber = phoneUtil.format(phoneNumber, PhoneNumberUtil.PhoneNumberFormat.E164).replace("+", "");
String medium = "msisdn";
strategy.findLocal(medium, canon_phoneNumber).ifPresent(lookupDataOpt -> {
identifier.addProperty("type", "m.id.user");
identifier.addProperty("user", lookupDataOpt.getMxid().getLocalPart());
identifier.remove("country");
identifier.remove("number");
});
} catch (NumberParseException e) {
throw new RuntimeException(e);
}
});
});
}
});
});
// invoke 'login' on homeserver
HttpPost httpPost = RestClientUtils.post(resolveProxyUrl(req), gson, reqJsonObject);
try (CloseableHttpResponse httpResponse = client.execute(httpPost)) {
// check http status
int status = httpResponse.getStatusLine().getStatusCode();
log.info("http status = {}", status);
if (status != 200) {
// try to get possible json error message from response
// otherwise just get returned plain error message
String errcode = String.valueOf(httpResponse.getStatusLine().getStatusCode());
String error = EntityUtils.toString(httpResponse.getEntity());
if (httpResponse.getEntity() != null) {
try {
JsonObject bodyJson = new JsonParser().parse(error).getAsJsonObject();
if (bodyJson.has("errcode")) {
errcode = bodyJson.get("errcode").getAsString();
}
if (bodyJson.has("error")) {
error = bodyJson.get("error").getAsString();
}
throw new RemoteLoginException(status, errcode, error, bodyJson);
} catch (JsonSyntaxException e) {
log.warn("Response body is not JSON");
}
}
throw new RemoteLoginException(status, errcode, error);
}
/// return response
JsonObject respJsonObject = parser.parseOptional(httpResponse).get();
return gson.toJson(respJsonObject);
} catch (IOException e) {
throw new RuntimeException(e);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -104,12 +104,19 @@ public class SessionRestController {
if (ThreePidMedium.PhoneNumber.is(medium)) {
SessionPhoneTokenRequestJson req = parser.parse(request, SessionPhoneTokenRequestJson.class);
return gson.toJson(new Sid(mgr.create(
ThreePid threepid = new ThreePid(req.getMedium(), req.getValue());
String sessionId = mgr.create(
request.getRemoteHost(),
new ThreePid(req.getMedium(), req.getValue()),
threepid,
req.getSecret(),
req.getAttempt(),
req.getNextLink())));
req.getNextLink());
JsonObject res = new JsonObject();
res.addProperty("sid", sessionId);
res.addProperty(threepid.getMedium(), threepid.getAddress());
return gson.toJson(res);
}
JsonObject obj = new JsonObject();

View File

@@ -38,7 +38,6 @@ import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.ContentType;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -59,14 +58,15 @@ public class DirectoryManager {
private List<IDirectoryProvider> providers;
private ClientDnsOverwrite dns;
private CloseableHttpClient client;
private Gson gson;
@Autowired
private CloseableHttpClient client;
@Autowired
public DirectoryManager(DirectoryConfig cfg, List<IDirectoryProvider> providers, ClientDnsOverwrite dns) {
this.cfg = cfg;
this.dns = dns;
this.client = HttpClients.custom().setUserAgent("mxisd").build(); //FIXME centralize
this.gson = GsonUtil.build();
this.providers = providers.stream().filter(IDirectoryProvider::isEnabled).collect(Collectors.toList());

View File

@@ -0,0 +1,43 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2017 Maxime Dor
*
* https://max.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.exception;
import com.google.gson.JsonObject;
public class RemoteLoginException extends MatrixException {
private JsonObject errorBodyMsgResp;
public RemoteLoginException(int status, String errorCode, String error) {
super(status, errorCode, error);
this.errorBodyMsgResp = null;
}
public RemoteLoginException(int status, String errorCode, String error, JsonObject errorBodyMsgResp) {
super(status, errorCode, error);
this.errorBodyMsgResp = errorBodyMsgResp;
}
public JsonObject getErrorBodyMsgResp() {
return errorBodyMsgResp;
}
}

View File

@@ -24,6 +24,7 @@ import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import io.kamax.matrix.MatrixID;
import io.kamax.mxisd.config.InvitationConfig;
import io.kamax.mxisd.dns.FederationDnsOverwrite;
import io.kamax.mxisd.exception.BadRequestException;
import io.kamax.mxisd.exception.MappingAlreadyExistsException;
@@ -72,6 +73,9 @@ public class InvitationManager {
private Map<String, IThreePidInviteReply> invitations = new ConcurrentHashMap<>();
@Autowired
private InvitationConfig cfg;
@Autowired
private IStorage storage;
@@ -137,7 +141,7 @@ public class InvitationManager {
log.error("Error when running background mapping refresh", t);
}
}
}, 5000L, TimeUnit.MILLISECONDS.convert(1, TimeUnit.MINUTES)); // FIXME make configurable
}, 5000L, TimeUnit.MILLISECONDS.convert(cfg.getResolution().getTimer(), TimeUnit.MINUTES));
}
@PreDestroy
@@ -204,6 +208,14 @@ public class InvitationManager {
return "https://" + domain + ":8448";
}
private Optional<SingleLookupReply> lookup3pid(String medium, String address) {
if (!cfg.getResolution().isRecursive()) {
log.warn("/!\\ /!\\ --- RECURSIVE INVITE RESOLUTION HAS BEEN DISABLED --- /!\\ /!\\");
}
return lookupMgr.find(medium, address, cfg.getResolution().isRecursive());
}
public synchronized IThreePidInviteReply storeInvite(IThreePidInvite invitation) { // TODO better sync
if (!notifMgr.isMediumSupported(invitation.getMedium())) {
throw new BadRequestException("Medium type " + invitation.getMedium() + " is not supported");
@@ -223,7 +235,7 @@ public class InvitationManager {
return reply;
}
Optional<?> result = lookupMgr.find(invitation.getMedium(), invitation.getAddress(), true);
Optional<SingleLookupReply> result = lookup3pid(invitation.getMedium(), invitation.getAddress());
if (result.isPresent()) {
log.info("Mapping for {}:{} already exists, refusing to store invite", invitation.getMedium(), invitation.getAddress());
throw new MappingAlreadyExistsException();
@@ -333,7 +345,7 @@ public class InvitationManager {
public void run() {
try {
log.info("Searching for mapping created since invite {} was created", getIdForLog(reply));
Optional<SingleLookupReply> result = lookupMgr.find(reply.getInvite().getMedium(), reply.getInvite().getAddress(), true);
Optional<SingleLookupReply> result = lookup3pid(reply.getInvite().getMedium(), reply.getInvite().getAddress());
if (result.isPresent()) {
SingleLookupReply lookup = result.get();
log.info("Found mapping for pending invite {}", getIdForLog(reply));

View File

@@ -159,6 +159,8 @@ public class RecursivePriorityLookupStrategy implements LookupStrategy {
for (IThreePidProvider provider : providers) {
Optional<SingleLookupReply> lookupDataOpt = provider.find(request);
if (lookupDataOpt.isPresent()) {
log.info("Found 3PID mapping: {medium: '{}', address: '{}', mxid: '{}'}",
request.getType(), request.getThreePid(), lookupDataOpt.get().getMxid().getId());
return lookupDataOpt;
}
}
@@ -169,9 +171,13 @@ public class RecursivePriorityLookupStrategy implements LookupStrategy {
(!cfg.getBridge().getRecursiveOnly() || isAllowedForRecursive(request.getRequester()))
) {
log.info("Using bridge failover for lookup");
return bridge.find(request);
Optional<SingleLookupReply> lookupDataOpt = bridge.find(request);
log.info("Found 3PID mapping: {medium: '{}', address: '{}', mxid: '{}'}",
request.getThreePid(), request.getId(), lookupDataOpt.get().getMxid().getId());
return lookupDataOpt;
}
log.info("No 3PID mapping found");
return Optional.empty();
}

View File

@@ -46,16 +46,14 @@ public class NotificationManager {
this.handlers = new HashMap<>();
handlers.forEach(h -> {
log.info("Found handler {} for medium {}", h.getId(), h.getMedium());
String handlerId = cfg.getHandler().get(h.getMedium());
if (StringUtils.isBlank(handlerId) || StringUtils.equals(handlerId, h.getId())) {
String handlerId = cfg.getHandler().getOrDefault(h.getMedium(), "raw");
if (StringUtils.equals(handlerId, h.getId())) {
this.handlers.put(h.getMedium(), h);
}
});
log.info("--- Notification handler ---");
this.handlers.forEach((k, v) -> {
log.info("\tHandler for {}: {}", k, v.getId());
});
this.handlers.forEach((k, v) -> log.info("\tHandler for {}: {}", k, v.getId()));
}
private INotificationHandler ensureMedium(String medium) {

View File

@@ -196,7 +196,7 @@ public class SessionMananger {
storage.updateThreePidSession(session.getDao());
log.info("Session {} has been validated locally", session.getId());
if (ThreePidMedium.PhoneNumber.is(session.getThreePid().getMedium()) && session.isValidated()) {
if (ThreePidMedium.PhoneNumber.is(session.getThreePid().getMedium()) && session.isValidated() && policy.toRemote()) {
createRemote(sid, secret);
// FIXME make the message configurable/customizable (templates?)
throw new MessageForClientException("You will receive a NEW code from another number. Enter it below");
@@ -379,7 +379,7 @@ public class SessionMananger {
if (o.has("validated_at")) {
ThreePid remoteThreePid = new ThreePid(o.get("medium").getAsString(), o.get("address").getAsString());
if (session.getThreePid().equals(remoteThreePid)) { // sanity check
if (!session.getThreePid().equals(remoteThreePid)) { // sanity check
throw new InternalServerError("Local 3PID " + session.getThreePid() + " and remote 3PID " + remoteThreePid + " do not match for session " + session.getId());
}

View File

@@ -0,0 +1,36 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2018 Kamax Sàrl
*
* https://www.kamax.io/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.mxisd.spring;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class CloseableHttpClientFactory {
@Bean
public CloseableHttpClient getClient() {
return HttpClients.custom().setUserAgent("mxisd").build();
}
}

View File

@@ -0,0 +1,38 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2018 Kamax Sàrl
*
* https://www.kamax.io/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package io.kamax.mxisd.threepid.connector.phone;
import org.springframework.stereotype.Component;
@Component
public class BlackholePhoneConnector implements IPhoneConnector {
@Override
public void send(String recipient, String content) {
//dev/null
}
@Override
public String getId() {
return "BLACKHOLE";
}
}

View File

@@ -20,9 +20,9 @@
package io.kamax.mxisd.util;
import com.google.gson.FieldNamingPolicy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.*;
import java.util.Optional;
public class GsonUtil {
@@ -30,4 +30,16 @@ public class GsonUtil {
return new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
}
public static Optional<JsonElement> findElement(JsonObject o, String key) {
return Optional.ofNullable(o.get(key));
}
public static Optional<JsonObject> findObj(JsonObject o, String key) {
return findElement(o, key).map(el -> el.isJsonObject() ? el.getAsJsonObject() : null);
}
public static Optional<JsonPrimitive> findPrimitive(JsonObject o, String key) {
return findElement(o, key).map(el -> el.isJsonPrimitive() ? el.getAsJsonPrimitive() : null);
}
}

View File

@@ -89,6 +89,46 @@ ldap:
email: ''
msisdn: ''
netiq:
enabled: false
filter: ''
connection:
host: ''
tls: false
port: 389
bindDn: ''
bindPassword: ''
baseDn: ''
attribute:
uid:
type: 'uid'
value: 'userPrincipalName'
name: 'displayName'
threepid:
email:
- 'mailPrimaryAddress'
- 'mail'
- 'otherMailbox'
msisdn:
- 'telephoneNumber'
- 'mobile'
- 'homePhone'
- 'otherTelephone'
- 'otherMobile'
- 'otherHomePhone'
auth:
filter: ''
directory:
attribute:
other: []
filter: ''
identity:
filter: ''
token: '%3pid'
medium:
email: ''
msisdn: ''
firebase:
enabled: false
@@ -224,6 +264,11 @@ view:
success: 'session/remote/checkSuccess'
failure: 'session/remote/checkFailure'
invite:
resolution:
recursive: true
timer: 1
storage:
backend: 'sqlite'