Compare commits
15 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
1e413af019 | ||
|
a0f8af820e | ||
|
5ef145212a | ||
|
91ccb75fa1 | ||
|
ac6f549618 | ||
|
7f9c7aa76d | ||
|
02688942fd | ||
|
48668bcd92 | ||
|
a9627121fa | ||
|
3fc86465f8 | ||
|
d93b546e3c | ||
|
ea15f24d41 | ||
|
290a32d640 | ||
|
10f9126cb6 | ||
|
c3385b38dc |
12
.travis.yml
12
.travis.yml
@@ -1,4 +1,8 @@
|
|||||||
language: groovy
|
language: java
|
||||||
|
before_cache:
|
||||||
jdk:
|
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
|
||||||
- oraclejdk8
|
- rm -fr $HOME/.gradle/caches/*/plugin-resolution/
|
||||||
|
cache:
|
||||||
|
directories:
|
||||||
|
- $HOME/.gradle/caches/
|
||||||
|
- $HOME/.gradle/wrapper/
|
||||||
|
@@ -66,6 +66,7 @@ buildscript {
|
|||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
maven { url "https://kamax.io/maven/releases/" }
|
maven { url "https://kamax.io/maven/releases/" }
|
||||||
|
maven { url "https://kamax.io/maven/snapshots/" }
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,7 +81,7 @@ dependencies {
|
|||||||
compile "org.springframework.boot:spring-boot-starter-thymeleaf:1.5.10.RELEASE"
|
compile "org.springframework.boot:spring-boot-starter-thymeleaf:1.5.10.RELEASE"
|
||||||
|
|
||||||
// Matrix Java SDK
|
// Matrix Java SDK
|
||||||
compile 'io.kamax:matrix-java-sdk:0.0.2'
|
compile 'io.kamax:matrix-java-sdk:0.0.11'
|
||||||
|
|
||||||
// ed25519 handling
|
// ed25519 handling
|
||||||
compile 'net.i2p.crypto:eddsa:0.1.0'
|
compile 'net.i2p.crypto:eddsa:0.1.0'
|
||||||
@@ -94,9 +95,6 @@ dependencies {
|
|||||||
// HTTP connections
|
// HTTP connections
|
||||||
compile 'org.apache.httpcomponents:httpclient:4.5.3'
|
compile 'org.apache.httpcomponents:httpclient:4.5.3'
|
||||||
|
|
||||||
// JSON
|
|
||||||
compile 'com.google.code.gson:gson:2.8.1'
|
|
||||||
|
|
||||||
// Phone numbers validation
|
// Phone numbers validation
|
||||||
compile 'com.googlecode.libphonenumber:libphonenumber:8.7.1'
|
compile 'com.googlecode.libphonenumber:libphonenumber:8.7.1'
|
||||||
|
|
||||||
@@ -119,6 +117,9 @@ dependencies {
|
|||||||
// PostgreSQL
|
// PostgreSQL
|
||||||
compile 'org.postgresql:postgresql:42.1.4'
|
compile 'org.postgresql:postgresql:42.1.4'
|
||||||
|
|
||||||
|
// MariaDB/MySQL
|
||||||
|
compile 'org.mariadb.jdbc:mariadb-java-client:2.1.2'
|
||||||
|
|
||||||
// Twilio SDK for SMS
|
// Twilio SDK for SMS
|
||||||
compile 'com.twilio.sdk:twilio:7.14.5'
|
compile 'com.twilio.sdk:twilio:7.14.5'
|
||||||
|
|
||||||
|
@@ -17,6 +17,7 @@
|
|||||||
- [SQL](backends/sql.md)
|
- [SQL](backends/sql.md)
|
||||||
- [REST](backends/rest.md)
|
- [REST](backends/rest.md)
|
||||||
- [Google Firebase](backends/firebase.md)
|
- [Google Firebase](backends/firebase.md)
|
||||||
|
- [Wordpress](backends/wordpress.md)
|
||||||
- Notifications
|
- Notifications
|
||||||
- Handlers
|
- Handlers
|
||||||
- [Basic](threepids/notifications/basic-handler.md)
|
- [Basic](threepids/notifications/basic-handler.md)
|
||||||
|
@@ -2,4 +2,5 @@
|
|||||||
- [Samba / Active Directory / LDAP](ldap.md)
|
- [Samba / Active Directory / LDAP](ldap.md)
|
||||||
- [SQL Databases](sql.md)
|
- [SQL Databases](sql.md)
|
||||||
- [Website / Web service / Web app](rest.md)
|
- [Website / Web service / Web app](rest.md)
|
||||||
- [Google Firebase](firebase.md)
|
- [Google Firebase](firebase.md)
|
||||||
|
- [Wordpress](wordpress.md)
|
55
docs/backends/wordpress.md
Normal file
55
docs/backends/wordpress.md
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
# Wordpress
|
||||||
|
This Identity store allows you to use user accounts registered on your Wordpress setup.
|
||||||
|
Two types of connections are required for full support:
|
||||||
|
- [REST API](https://developer.wordpress.org/rest-api/) with JWT authentication
|
||||||
|
- Direct SQL access
|
||||||
|
|
||||||
|
This Identity store supports the following features:
|
||||||
|
- [Authentication](../features/authentication.md)
|
||||||
|
- [Directory](../features/directory-users.md)
|
||||||
|
- [Identity](../features/identity.md)
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
- [Wordpress](https://wordpress.org/download/) >= 4.4
|
||||||
|
- Permalink structure set to `Post Name`
|
||||||
|
- [JWT Auth plugin for REST API](https://wordpress.org/plugins/jwt-authentication-for-wp-rest-api/)
|
||||||
|
- SQL Credentials to the Wordpress Database
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
### Wordpress
|
||||||
|
#### JWT Auth
|
||||||
|
Set a JWT secret into `wp-config.php` like so:
|
||||||
|
```
|
||||||
|
define('JWT_AUTH_SECRET_KEY', 'your-top-secret-key');
|
||||||
|
```
|
||||||
|
`your-top-secret-key` should be set to a randomly generated value which is kept secret.
|
||||||
|
|
||||||
|
#### Rewrite of `index.php`
|
||||||
|
Wordpress is normally configured with rewrite of `index.php` so it does not appear in URLs.
|
||||||
|
If this is not the case for your installation, the mxisd URL will need to be appended with `/index.php`
|
||||||
|
|
||||||
|
### mxisd
|
||||||
|
Enable in the configuration:
|
||||||
|
```
|
||||||
|
wordpress.enabled: true
|
||||||
|
```
|
||||||
|
Configure the URL to your Wordpress installation - see above about added `/index.php`:
|
||||||
|
```
|
||||||
|
wordpress.rest.base: 'http://localhost:8080'
|
||||||
|
```
|
||||||
|
Configure the SQL connection to your Wordpress database:
|
||||||
|
```
|
||||||
|
wordpress.sql.connection: '//127.0.0.1/wordpress?user=root&password=example'
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
By default, MySQL database is expected. If you use another database, use:
|
||||||
|
```
|
||||||
|
wordpress.sql.type: 'jdbc-scheme'
|
||||||
|
```
|
||||||
|
With possible values:
|
||||||
|
- `mysql`
|
||||||
|
- `mariadb`
|
||||||
|
- `postgresql`
|
||||||
|
- `sqlite`
|
@@ -115,8 +115,8 @@ Steps of user authentication using a 3PID:
|
|||||||
- Homeserver
|
- Homeserver
|
||||||
- Compatible Identity backends:
|
- Compatible Identity backends:
|
||||||
- LDAP
|
- LDAP
|
||||||
- SQL
|
|
||||||
- REST
|
- REST
|
||||||
|
- Wordpress
|
||||||
|
|
||||||
### Configuration
|
### Configuration
|
||||||
|
|
||||||
@@ -160,12 +160,3 @@ value is the base internal URL of the Homeserver, without any /_matrix/.. or tra
|
|||||||
|
|
||||||
#### Backends
|
#### Backends
|
||||||
The Backends should be configured as described in the documentation of the [Directory User](directory-users.md) feature.
|
The Backends should be configured as described in the documentation of the [Directory User](directory-users.md) feature.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@@ -219,6 +219,9 @@ For each query, `type` can be used to tell mxisd how to process the ID column:
|
|||||||
#### REST
|
#### REST
|
||||||
See the [dedicated document](../backends/rest.md)
|
See the [dedicated document](../backends/rest.md)
|
||||||
|
|
||||||
|
#### Wordpress
|
||||||
|
See the [dedicated document](../backends/wordpress.md)
|
||||||
|
|
||||||
## Next steps
|
## Next steps
|
||||||
### Homeserver results
|
### Homeserver results
|
||||||
You can configure if the Homeserver should be queried at all when doing a directory search.
|
You can configure if the Homeserver should be queried at all when doing a directory search.
|
||||||
|
12
docs/features/profile.md
Normal file
12
docs/features/profile.md
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# Profile enhancement
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
### Reverse proxy
|
||||||
|
|
||||||
|
#### Apache
|
||||||
|
```
|
||||||
|
ProxyPassMatch "^/_matrix/client/r0/profile/([^/]+)$" "http://127.0.0.1:8090/_matrix/client/r0/profile/$1"
|
||||||
|
ProxyPassMatch "^/_matrix/client/r0/profile/([^/]+)/(.+)" "http://127.0.0.1:8008/_matrix/client/r0/profile/$1/$2"
|
||||||
|
|
||||||
|
```
|
@@ -143,3 +143,4 @@ Use your Identity stores:
|
|||||||
- [SQL Database](backends/sql.md)
|
- [SQL Database](backends/sql.md)
|
||||||
- [Website / Web service / Web app](backends/rest.md)
|
- [Website / Web service / Web app](backends/rest.md)
|
||||||
- [Google Firebase](backends/firebase.md)
|
- [Google Firebase](backends/firebase.md)
|
||||||
|
- [Wordpress](backends/wordpress.md)
|
||||||
|
@@ -1,69 +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;
|
|
||||||
|
|
||||||
// FIXME this should be in matrix-java-sdk
|
|
||||||
public class ThreePid {
|
|
||||||
|
|
||||||
private String medium;
|
|
||||||
private String address;
|
|
||||||
|
|
||||||
public ThreePid(ThreePid tpid) {
|
|
||||||
this(tpid.getMedium(), tpid.getAddress());
|
|
||||||
}
|
|
||||||
|
|
||||||
public ThreePid(String medium, String address) {
|
|
||||||
this.medium = medium;
|
|
||||||
this.address = address;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getMedium() {
|
|
||||||
return medium;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getAddress() {
|
|
||||||
return address;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return getMedium() + ":" + getAddress();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (this == o) return true;
|
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
|
||||||
|
|
||||||
ThreePid threePid = (ThreePid) o;
|
|
||||||
|
|
||||||
if (!medium.equals(threePid.medium)) return false;
|
|
||||||
return address.equals(threePid.address);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
int result = medium.hashCode();
|
|
||||||
result = 31 * result + address.hashCode();
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@@ -21,8 +21,9 @@
|
|||||||
package io.kamax.mxisd.auth;
|
package io.kamax.mxisd.auth;
|
||||||
|
|
||||||
import io.kamax.matrix.MatrixID;
|
import io.kamax.matrix.MatrixID;
|
||||||
|
import io.kamax.matrix.ThreePid;
|
||||||
import io.kamax.matrix._MatrixID;
|
import io.kamax.matrix._MatrixID;
|
||||||
import io.kamax.mxisd.ThreePid;
|
import io.kamax.matrix._ThreePid;
|
||||||
import io.kamax.mxisd.UserIdType;
|
import io.kamax.mxisd.UserIdType;
|
||||||
import io.kamax.mxisd.auth.provider.AuthenticatorProvider;
|
import io.kamax.mxisd.auth.provider.AuthenticatorProvider;
|
||||||
import io.kamax.mxisd.auth.provider.BackendAuthResult;
|
import io.kamax.mxisd.auth.provider.BackendAuthResult;
|
||||||
@@ -52,7 +53,7 @@ public class AuthManager {
|
|||||||
private InvitationManager invMgr;
|
private InvitationManager invMgr;
|
||||||
|
|
||||||
public UserAuthResult authenticate(String id, String password) {
|
public UserAuthResult authenticate(String id, String password) {
|
||||||
_MatrixID mxid = new MatrixID(id);
|
_MatrixID mxid = MatrixID.asAcceptable(id);
|
||||||
for (AuthenticatorProvider provider : providers) {
|
for (AuthenticatorProvider provider : providers) {
|
||||||
if (!provider.isEnabled()) {
|
if (!provider.isEnabled()) {
|
||||||
continue;
|
continue;
|
||||||
@@ -63,16 +64,16 @@ public class AuthManager {
|
|||||||
|
|
||||||
String mxId;
|
String mxId;
|
||||||
if (UserIdType.Localpart.is(result.getId().getType())) {
|
if (UserIdType.Localpart.is(result.getId().getType())) {
|
||||||
mxId = new MatrixID(result.getId().getValue(), mxCfg.getDomain()).getId();
|
mxId = MatrixID.from(result.getId().getValue(), mxCfg.getDomain()).acceptable().getId();
|
||||||
} else if (UserIdType.MatrixID.is(result.getId().getType())) {
|
} else if (UserIdType.MatrixID.is(result.getId().getType())) {
|
||||||
mxId = new MatrixID(result.getId().getValue()).getId();
|
mxId = MatrixID.asAcceptable(result.getId().getValue()).getId();
|
||||||
} else {
|
} else {
|
||||||
log.warn("Unsupported User ID type {} for backend {}", result.getId().getType(), provider.getClass().getSimpleName());
|
log.warn("Unsupported User ID type {} for backend {}", result.getId().getType(), provider.getClass().getSimpleName());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
UserAuthResult authResult = new UserAuthResult().success(result.getProfile().getDisplayName());
|
UserAuthResult authResult = new UserAuthResult().success(result.getProfile().getDisplayName());
|
||||||
for (ThreePid pid : result.getProfile().getThreePids()) {
|
for (_ThreePid pid : result.getProfile().getThreePids()) {
|
||||||
authResult.withThreePid(pid.getMedium(), pid.getAddress());
|
authResult.withThreePid(pid.getMedium(), pid.getAddress());
|
||||||
}
|
}
|
||||||
log.info("{} was authenticated by {}, publishing 3PID mappings, if any", id, provider.getClass().getSimpleName());
|
log.info("{} was authenticated by {}, publishing 3PID mappings, if any", id, provider.getClass().getSimpleName());
|
||||||
|
@@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
package io.kamax.mxisd.auth;
|
package io.kamax.mxisd.auth;
|
||||||
|
|
||||||
import io.kamax.mxisd.ThreePid;
|
import io.kamax.matrix.ThreePid;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
@@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
package io.kamax.mxisd.auth.provider;
|
package io.kamax.mxisd.auth.provider;
|
||||||
|
|
||||||
import io.kamax.mxisd.ThreePid;
|
import io.kamax.matrix.ThreePid;
|
||||||
import io.kamax.mxisd.UserID;
|
import io.kamax.mxisd.UserID;
|
||||||
import io.kamax.mxisd.UserIdType;
|
import io.kamax.mxisd.UserIdType;
|
||||||
|
|
||||||
@@ -66,7 +66,6 @@ public class BackendAuthResult {
|
|||||||
public void succeed(String id, String type, String displayName) {
|
public void succeed(String id, String type, String displayName) {
|
||||||
this.success = true;
|
this.success = true;
|
||||||
this.id = new UserID(type, id);
|
this.id = new UserID(type, id);
|
||||||
this.profile = new BackendAuthProfile();
|
|
||||||
this.profile.displayName = displayName;
|
this.profile.displayName = displayName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -23,9 +23,9 @@ package io.kamax.mxisd.backend.firebase;
|
|||||||
import com.google.firebase.auth.UserInfo;
|
import com.google.firebase.auth.UserInfo;
|
||||||
import com.google.i18n.phonenumbers.NumberParseException;
|
import com.google.i18n.phonenumbers.NumberParseException;
|
||||||
import com.google.i18n.phonenumbers.PhoneNumberUtil;
|
import com.google.i18n.phonenumbers.PhoneNumberUtil;
|
||||||
|
import io.kamax.matrix.ThreePid;
|
||||||
import io.kamax.matrix.ThreePidMedium;
|
import io.kamax.matrix.ThreePidMedium;
|
||||||
import io.kamax.matrix._MatrixID;
|
import io.kamax.matrix._MatrixID;
|
||||||
import io.kamax.mxisd.ThreePid;
|
|
||||||
import io.kamax.mxisd.UserIdType;
|
import io.kamax.mxisd.UserIdType;
|
||||||
import io.kamax.mxisd.auth.provider.AuthenticatorProvider;
|
import io.kamax.mxisd.auth.provider.AuthenticatorProvider;
|
||||||
import io.kamax.mxisd.auth.provider.BackendAuthResult;
|
import io.kamax.mxisd.auth.provider.BackendAuthResult;
|
||||||
|
@@ -22,14 +22,14 @@ package io.kamax.mxisd.backend.ldap;
|
|||||||
|
|
||||||
import com.google.i18n.phonenumbers.NumberParseException;
|
import com.google.i18n.phonenumbers.NumberParseException;
|
||||||
import com.google.i18n.phonenumbers.PhoneNumberUtil;
|
import com.google.i18n.phonenumbers.PhoneNumberUtil;
|
||||||
|
import io.kamax.matrix.ThreePid;
|
||||||
import io.kamax.matrix.ThreePidMedium;
|
import io.kamax.matrix.ThreePidMedium;
|
||||||
import io.kamax.matrix._MatrixID;
|
import io.kamax.matrix._MatrixID;
|
||||||
import io.kamax.mxisd.ThreePid;
|
|
||||||
import io.kamax.mxisd.UserIdType;
|
import io.kamax.mxisd.UserIdType;
|
||||||
import io.kamax.mxisd.auth.provider.AuthenticatorProvider;
|
import io.kamax.mxisd.auth.provider.AuthenticatorProvider;
|
||||||
import io.kamax.mxisd.auth.provider.BackendAuthResult;
|
import io.kamax.mxisd.auth.provider.BackendAuthResult;
|
||||||
import io.kamax.mxisd.config.MatrixConfig;
|
import io.kamax.mxisd.config.MatrixConfig;
|
||||||
import io.kamax.mxisd.config.ldap.generic.GenericLdapConfig;
|
import io.kamax.mxisd.config.ldap.LdapConfig;
|
||||||
import io.kamax.mxisd.util.GsonUtil;
|
import io.kamax.mxisd.util.GsonUtil;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
import org.apache.directory.api.ldap.model.cursor.CursorException;
|
import org.apache.directory.api.ldap.model.cursor.CursorException;
|
||||||
@@ -59,7 +59,7 @@ public class LdapAuthProvider extends LdapBackend implements AuthenticatorProvid
|
|||||||
private PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
|
private PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public LdapAuthProvider(GenericLdapConfig cfg, MatrixConfig mxCfg) {
|
public LdapAuthProvider(LdapConfig cfg, MatrixConfig mxCfg) {
|
||||||
super(cfg, mxCfg);
|
super(cfg, mxCfg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -22,7 +22,6 @@ package io.kamax.mxisd.backend.ldap;
|
|||||||
|
|
||||||
import io.kamax.mxisd.config.MatrixConfig;
|
import io.kamax.mxisd.config.MatrixConfig;
|
||||||
import io.kamax.mxisd.config.ldap.LdapConfig;
|
import io.kamax.mxisd.config.ldap.LdapConfig;
|
||||||
import io.kamax.mxisd.config.ldap.generic.GenericLdapConfig;
|
|
||||||
import io.kamax.mxisd.controller.directory.v1.io.UserDirectorySearchResult;
|
import io.kamax.mxisd.controller.directory.v1.io.UserDirectorySearchResult;
|
||||||
import io.kamax.mxisd.directory.IDirectoryProvider;
|
import io.kamax.mxisd.directory.IDirectoryProvider;
|
||||||
import io.kamax.mxisd.exception.InternalServerError;
|
import io.kamax.mxisd.exception.InternalServerError;
|
||||||
@@ -49,7 +48,7 @@ public class LdapDirectoryProvider extends LdapBackend implements IDirectoryProv
|
|||||||
private Logger log = LoggerFactory.getLogger(LdapDirectoryProvider.class);
|
private Logger log = LoggerFactory.getLogger(LdapDirectoryProvider.class);
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public LdapDirectoryProvider(GenericLdapConfig cfg, MatrixConfig mxCfg) {
|
public LdapDirectoryProvider(LdapConfig cfg, MatrixConfig mxCfg) {
|
||||||
super(cfg, mxCfg);
|
super(cfg, mxCfg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -21,7 +21,7 @@
|
|||||||
package io.kamax.mxisd.backend.ldap;
|
package io.kamax.mxisd.backend.ldap;
|
||||||
|
|
||||||
import io.kamax.mxisd.config.MatrixConfig;
|
import io.kamax.mxisd.config.MatrixConfig;
|
||||||
import io.kamax.mxisd.config.ldap.generic.GenericLdapConfig;
|
import io.kamax.mxisd.config.ldap.LdapConfig;
|
||||||
import io.kamax.mxisd.exception.InternalServerError;
|
import io.kamax.mxisd.exception.InternalServerError;
|
||||||
import io.kamax.mxisd.lookup.SingleLookupReply;
|
import io.kamax.mxisd.lookup.SingleLookupReply;
|
||||||
import io.kamax.mxisd.lookup.SingleLookupRequest;
|
import io.kamax.mxisd.lookup.SingleLookupRequest;
|
||||||
@@ -49,7 +49,7 @@ public class LdapThreePidProvider extends LdapBackend implements IThreePidProvid
|
|||||||
|
|
||||||
private Logger log = LoggerFactory.getLogger(LdapThreePidProvider.class);
|
private Logger log = LoggerFactory.getLogger(LdapThreePidProvider.class);
|
||||||
|
|
||||||
public LdapThreePidProvider(GenericLdapConfig cfg, MatrixConfig mxCfg) {
|
public LdapThreePidProvider(LdapConfig cfg, MatrixConfig mxCfg) {
|
||||||
super(cfg, mxCfg);
|
super(cfg, mxCfg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -23,6 +23,7 @@ package io.kamax.mxisd.backend.memory;
|
|||||||
import io.kamax.matrix.MatrixID;
|
import io.kamax.matrix.MatrixID;
|
||||||
import io.kamax.matrix.ThreePid;
|
import io.kamax.matrix.ThreePid;
|
||||||
import io.kamax.matrix._MatrixID;
|
import io.kamax.matrix._MatrixID;
|
||||||
|
import io.kamax.matrix._ThreePid;
|
||||||
import io.kamax.mxisd.UserIdType;
|
import io.kamax.mxisd.UserIdType;
|
||||||
import io.kamax.mxisd.auth.provider.AuthenticatorProvider;
|
import io.kamax.mxisd.auth.provider.AuthenticatorProvider;
|
||||||
import io.kamax.mxisd.auth.provider.BackendAuthResult;
|
import io.kamax.mxisd.auth.provider.BackendAuthResult;
|
||||||
@@ -30,22 +31,28 @@ import io.kamax.mxisd.config.MatrixConfig;
|
|||||||
import io.kamax.mxisd.config.memory.MemoryIdentityConfig;
|
import io.kamax.mxisd.config.memory.MemoryIdentityConfig;
|
||||||
import io.kamax.mxisd.config.memory.MemoryStoreConfig;
|
import io.kamax.mxisd.config.memory.MemoryStoreConfig;
|
||||||
import io.kamax.mxisd.config.memory.MemoryThreePid;
|
import io.kamax.mxisd.config.memory.MemoryThreePid;
|
||||||
|
import io.kamax.mxisd.controller.directory.v1.io.UserDirectorySearchResult;
|
||||||
|
import io.kamax.mxisd.directory.IDirectoryProvider;
|
||||||
import io.kamax.mxisd.lookup.SingleLookupReply;
|
import io.kamax.mxisd.lookup.SingleLookupReply;
|
||||||
import io.kamax.mxisd.lookup.SingleLookupRequest;
|
import io.kamax.mxisd.lookup.SingleLookupRequest;
|
||||||
import io.kamax.mxisd.lookup.ThreePidMapping;
|
import io.kamax.mxisd.lookup.ThreePidMapping;
|
||||||
import io.kamax.mxisd.lookup.provider.IThreePidProvider;
|
import io.kamax.mxisd.lookup.provider.IThreePidProvider;
|
||||||
|
import io.kamax.mxisd.profile.ProfileProvider;
|
||||||
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;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class MemoryIdentityStore implements AuthenticatorProvider, IThreePidProvider {
|
public class MemoryIdentityStore implements AuthenticatorProvider, IDirectoryProvider, IThreePidProvider, ProfileProvider {
|
||||||
|
|
||||||
private final Logger logger = LoggerFactory.getLogger(MemoryIdentityStore.class);
|
private final Logger logger = LoggerFactory.getLogger(MemoryIdentityStore.class);
|
||||||
|
|
||||||
@@ -59,7 +66,9 @@ public class MemoryIdentityStore implements AuthenticatorProvider, IThreePidProv
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Optional<MemoryIdentityConfig> findByUsername(String username) {
|
public Optional<MemoryIdentityConfig> findByUsername(String username) {
|
||||||
return cfg.getIdentities().stream().filter(id -> StringUtils.equals(id.getUsername(), username)).findFirst();
|
return cfg.getIdentities().stream()
|
||||||
|
.filter(id -> StringUtils.equals(id.getUsername(), username))
|
||||||
|
.findFirst();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -67,6 +76,56 @@ public class MemoryIdentityStore implements AuthenticatorProvider, IThreePidProv
|
|||||||
return cfg.isEnabled();
|
return cfg.isEnabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private UserDirectorySearchResult search(
|
||||||
|
Predicate<MemoryIdentityConfig> predicate,
|
||||||
|
Function<MemoryIdentityConfig, UserDirectorySearchResult.Result> mapper
|
||||||
|
) {
|
||||||
|
UserDirectorySearchResult search = new UserDirectorySearchResult();
|
||||||
|
cfg.getIdentities().stream().filter(predicate).map(mapper).forEach(search::addResult);
|
||||||
|
return search;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserDirectorySearchResult searchByDisplayName(String query) {
|
||||||
|
return search(
|
||||||
|
entry -> StringUtils.containsIgnoreCase(entry.getUsername(), query),
|
||||||
|
entry -> {
|
||||||
|
UserDirectorySearchResult.Result result = new UserDirectorySearchResult.Result();
|
||||||
|
result.setUserId(MatrixID.from(entry.getUsername(), mxCfg.getDomain()).acceptable().getId());
|
||||||
|
result.setDisplayName(entry.getUsername());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserDirectorySearchResult searchBy3pid(String query) {
|
||||||
|
return search(
|
||||||
|
entry -> entry.getThreepids().stream()
|
||||||
|
.anyMatch(tpid -> StringUtils.containsIgnoreCase(tpid.getAddress(), query)),
|
||||||
|
entry -> {
|
||||||
|
UserDirectorySearchResult.Result result = new UserDirectorySearchResult.Result();
|
||||||
|
result.setUserId(MatrixID.from(entry.getUsername(), mxCfg.getDomain()).acceptable().getId());
|
||||||
|
result.setDisplayName(entry.getUsername());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<_ThreePid> getThreepids(_MatrixID mxid) {
|
||||||
|
List<_ThreePid> l = new ArrayList<>();
|
||||||
|
findByUsername(mxid.getLocalPart()).ifPresent(c -> l.addAll(c.getThreepids()));
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getRoles(_MatrixID mxid) {
|
||||||
|
List<String> l = new ArrayList<>();
|
||||||
|
findByUsername(mxid.getLocalPart()).ifPresent(c -> l.addAll(c.getRoles()));
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isLocal() {
|
public boolean isLocal() {
|
||||||
return true;
|
return true;
|
||||||
@@ -103,7 +162,10 @@ public class MemoryIdentityStore implements AuthenticatorProvider, IThreePidProv
|
|||||||
if (!StringUtils.equals(id.getUsername(), mxid.getLocalPart())) {
|
if (!StringUtils.equals(id.getUsername(), mxid.getLocalPart())) {
|
||||||
return BackendAuthResult.failure();
|
return BackendAuthResult.failure();
|
||||||
} else {
|
} else {
|
||||||
return BackendAuthResult.success(mxid.getId(), UserIdType.MatrixID, "");
|
BackendAuthResult result = new BackendAuthResult();
|
||||||
|
id.getThreepids().forEach(tpid -> result.withThreePid(new ThreePid(tpid.getMedium(), tpid.getAddress())));
|
||||||
|
result.succeed(mxid.getId(), UserIdType.MatrixID.getId(), "");
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
}).orElseGet(BackendAuthResult::failure);
|
}).orElseGet(BackendAuthResult::failure);
|
||||||
}
|
}
|
||||||
|
@@ -21,12 +21,16 @@
|
|||||||
package io.kamax.mxisd.backend.sql;
|
package io.kamax.mxisd.backend.sql;
|
||||||
|
|
||||||
import io.kamax.matrix.MatrixID;
|
import io.kamax.matrix.MatrixID;
|
||||||
|
import io.kamax.matrix.ThreePid;
|
||||||
|
import io.kamax.matrix._MatrixID;
|
||||||
|
import io.kamax.matrix._ThreePid;
|
||||||
import io.kamax.mxisd.config.MatrixConfig;
|
import io.kamax.mxisd.config.MatrixConfig;
|
||||||
import io.kamax.mxisd.config.sql.SqlConfig;
|
import io.kamax.mxisd.config.sql.SqlConfig;
|
||||||
import io.kamax.mxisd.lookup.SingleLookupReply;
|
import io.kamax.mxisd.lookup.SingleLookupReply;
|
||||||
import io.kamax.mxisd.lookup.SingleLookupRequest;
|
import io.kamax.mxisd.lookup.SingleLookupRequest;
|
||||||
import io.kamax.mxisd.lookup.ThreePidMapping;
|
import io.kamax.mxisd.lookup.ThreePidMapping;
|
||||||
import io.kamax.mxisd.lookup.provider.IThreePidProvider;
|
import io.kamax.mxisd.lookup.provider.IThreePidProvider;
|
||||||
|
import io.kamax.mxisd.profile.ProfileProvider;
|
||||||
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;
|
||||||
@@ -36,10 +40,11 @@ import java.sql.PreparedStatement;
|
|||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
public abstract class SqlThreePidProvider implements IThreePidProvider {
|
public abstract class SqlThreePidProvider implements IThreePidProvider, ProfileProvider {
|
||||||
|
|
||||||
private Logger log = LoggerFactory.getLogger(SqlThreePidProvider.class);
|
private Logger log = LoggerFactory.getLogger(SqlThreePidProvider.class);
|
||||||
|
|
||||||
@@ -109,4 +114,31 @@ public abstract class SqlThreePidProvider implements IThreePidProvider {
|
|||||||
return new ArrayList<>();
|
return new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<_ThreePid> getThreepids(_MatrixID mxid) {
|
||||||
|
List<_ThreePid> threepids = new ArrayList<>();
|
||||||
|
|
||||||
|
String stmtSql = cfg.getProfile().getThreepid().getQuery();
|
||||||
|
try (Connection conn = pool.get()) {
|
||||||
|
PreparedStatement stmt = conn.prepareStatement(stmtSql);
|
||||||
|
stmt.setString(1, mxid.getId());
|
||||||
|
|
||||||
|
ResultSet rSet = stmt.executeQuery();
|
||||||
|
while (rSet.next()) {
|
||||||
|
String medium = rSet.getString("medium");
|
||||||
|
String address = rSet.getString("address");
|
||||||
|
threepids.add(new ThreePid(medium, address));
|
||||||
|
}
|
||||||
|
|
||||||
|
return threepids;
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getRoles(_MatrixID mxid) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -32,10 +32,10 @@ import java.sql.PreparedStatement;
|
|||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class SynapseSqliteDirectoryProvider extends GenericSqlDirectoryProvider {
|
public class SynapseSqlDirectoryProvider extends GenericSqlDirectoryProvider {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public SynapseSqliteDirectoryProvider(SynapseSqlProviderConfig cfg, MatrixConfig mxCfg) {
|
public SynapseSqlDirectoryProvider(SynapseSqlProviderConfig cfg, MatrixConfig mxCfg) {
|
||||||
super(cfg, mxCfg);
|
super(cfg, mxCfg);
|
||||||
|
|
||||||
if (StringUtils.equals("sqlite", cfg.getType())) {
|
if (StringUtils.equals("sqlite", cfg.getType())) {
|
@@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* 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.wordpress;
|
||||||
|
|
||||||
|
public class WordpressAuthData {
|
||||||
|
|
||||||
|
public String token;
|
||||||
|
private String userEmail;
|
||||||
|
private String userNicename;
|
||||||
|
private String userDisplayName;
|
||||||
|
|
||||||
|
public String getToken() {
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setToken(String token) {
|
||||||
|
this.token = token;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUserEmail() {
|
||||||
|
return userEmail;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUserEmail(String userEmail) {
|
||||||
|
this.userEmail = userEmail;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUserNicename() {
|
||||||
|
return userNicename;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUserNicename(String userNicename) {
|
||||||
|
this.userNicename = userNicename;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUserDisplayName() {
|
||||||
|
return userDisplayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUserDisplayName(String userDisplayName) {
|
||||||
|
this.userDisplayName = userDisplayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* 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.wordpress;
|
||||||
|
|
||||||
|
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 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;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class WordpressAuthProvider implements AuthenticatorProvider {
|
||||||
|
|
||||||
|
private final Logger log = LoggerFactory.getLogger(WordpressAuthProvider.class);
|
||||||
|
|
||||||
|
private WordpressRestBackend wordpress;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public WordpressAuthProvider(WordpressRestBackend wordpress) {
|
||||||
|
this.wordpress = wordpress;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return wordpress.isEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BackendAuthResult authenticate(_MatrixID mxid, String password) {
|
||||||
|
try {
|
||||||
|
WordpressAuthData data = wordpress.authenticate(mxid.getLocalPart(), password);
|
||||||
|
BackendAuthResult result = new BackendAuthResult();
|
||||||
|
if (StringUtils.isNotBlank(data.getUserEmail())) {
|
||||||
|
result.withThreePid(new ThreePid("email", data.getUserEmail()));
|
||||||
|
}
|
||||||
|
result.succeed(mxid.getId(), UserIdType.MatrixID.getId(), data.getUserDisplayName());
|
||||||
|
return result;
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
log.error("Authentication failed for {}: {}", mxid.getId(), e.getMessage());
|
||||||
|
return BackendAuthResult.failure();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,116 @@
|
|||||||
|
/*
|
||||||
|
* 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.wordpress;
|
||||||
|
|
||||||
|
import io.kamax.matrix.MatrixID;
|
||||||
|
import io.kamax.mxisd.config.MatrixConfig;
|
||||||
|
import io.kamax.mxisd.config.wordpress.WordpressConfig;
|
||||||
|
import io.kamax.mxisd.controller.directory.v1.io.UserDirectorySearchResult;
|
||||||
|
import io.kamax.mxisd.directory.IDirectoryProvider;
|
||||||
|
import io.kamax.mxisd.exception.InternalServerError;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class WordpressDirectoryProvider implements IDirectoryProvider {
|
||||||
|
|
||||||
|
private final Logger log = LoggerFactory.getLogger(WordpressDirectoryProvider.class);
|
||||||
|
|
||||||
|
private WordpressConfig cfg;
|
||||||
|
private WordressSqlBackend wordpress;
|
||||||
|
private MatrixConfig mxCfg;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public WordpressDirectoryProvider(WordpressConfig cfg, WordressSqlBackend wordpress, MatrixConfig mxCfg) {
|
||||||
|
this.cfg = cfg;
|
||||||
|
this.wordpress = wordpress;
|
||||||
|
this.mxCfg = mxCfg;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return wordpress.isEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setParameters(PreparedStatement stmt, String searchTerm) throws SQLException {
|
||||||
|
for (int i = 1; i <= stmt.getParameterMetaData().getParameterCount(); i++) {
|
||||||
|
stmt.setString(i, "%" + searchTerm + "%");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Optional<UserDirectorySearchResult.Result> processRow(ResultSet rSet) throws SQLException {
|
||||||
|
UserDirectorySearchResult.Result item = new UserDirectorySearchResult.Result();
|
||||||
|
item.setUserId(rSet.getString(1));
|
||||||
|
item.setDisplayName(rSet.getString(2));
|
||||||
|
return Optional.of(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserDirectorySearchResult search(String searchTerm, String query) {
|
||||||
|
try (Connection conn = wordpress.getConnection()) {
|
||||||
|
log.info("Will execute query: {}", query);
|
||||||
|
try (PreparedStatement stmt = conn.prepareStatement(query)) {
|
||||||
|
setParameters(stmt, searchTerm);
|
||||||
|
|
||||||
|
try (ResultSet rSet = stmt.executeQuery()) {
|
||||||
|
UserDirectorySearchResult result = new UserDirectorySearchResult();
|
||||||
|
result.setLimited(false);
|
||||||
|
|
||||||
|
while (rSet.next()) {
|
||||||
|
processRow(rSet).ifPresent(e -> {
|
||||||
|
try {
|
||||||
|
e.setUserId(MatrixID.from(e.getUserId(), mxCfg.getDomain()).valid().getId());
|
||||||
|
result.addResult(e);
|
||||||
|
} catch (IllegalArgumentException ex) {
|
||||||
|
log.warn("Ignoring result {} - Invalid characters for a Matrix ID", e.getUserId());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
throw new InternalServerError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserDirectorySearchResult searchByDisplayName(String searchTerm) {
|
||||||
|
log.info("Searching users by display name using '{}'", searchTerm);
|
||||||
|
return search(searchTerm, cfg.getSql().getQuery().getDirectory().get("name"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserDirectorySearchResult searchBy3pid(String searchTerm) {
|
||||||
|
log.info("Searching users by 3PID using '{}'", searchTerm);
|
||||||
|
return search(searchTerm, cfg.getSql().getQuery().getDirectory().get("threepid"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,143 @@
|
|||||||
|
/*
|
||||||
|
* 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.wordpress;
|
||||||
|
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import io.kamax.matrix.json.GsonUtil;
|
||||||
|
import io.kamax.matrix.json.InvalidJsonException;
|
||||||
|
import io.kamax.mxisd.config.wordpress.WordpressConfig;
|
||||||
|
import io.kamax.mxisd.util.RestClientUtils;
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||||
|
import org.apache.http.client.methods.HttpGet;
|
||||||
|
import org.apache.http.client.methods.HttpPost;
|
||||||
|
import org.apache.http.client.methods.HttpRequestBase;
|
||||||
|
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;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class WordpressRestBackend {
|
||||||
|
|
||||||
|
private final Logger log = LoggerFactory.getLogger(WordpressRestBackend.class);
|
||||||
|
private final String jsonPath = "/wp-json";
|
||||||
|
private final String jwtPath = "/jwt-auth/v1";
|
||||||
|
|
||||||
|
private WordpressConfig cfg;
|
||||||
|
private CloseableHttpClient client;
|
||||||
|
|
||||||
|
private String jsonEndpoint;
|
||||||
|
private String jwtEndpoint;
|
||||||
|
|
||||||
|
private String token;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public WordpressRestBackend(WordpressConfig cfg, CloseableHttpClient client) {
|
||||||
|
this.cfg = cfg;
|
||||||
|
this.client = client;
|
||||||
|
|
||||||
|
if (!cfg.isEnabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonEndpoint = cfg.getRest().getBase() + jsonPath;
|
||||||
|
jwtEndpoint = jsonEndpoint + jwtPath;
|
||||||
|
validateConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateConfig() {
|
||||||
|
log.info("Validating JWT auth endpoint");
|
||||||
|
try (CloseableHttpResponse res = client.execute(new HttpGet(jwtEndpoint))) {
|
||||||
|
int status = res.getStatusLine().getStatusCode();
|
||||||
|
if (status != 200) {
|
||||||
|
log.warn("JWT auth endpoint check failed: Got status code {}", status);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String data = EntityUtils.toString(res.getEntity());
|
||||||
|
if (StringUtils.isBlank(data)) {
|
||||||
|
log.warn("JWT auth endpoint check failed: Got no/empty body data");
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonObject body = GsonUtil.parseObj(data);
|
||||||
|
if (!body.has("namespace")) {
|
||||||
|
log.warn("JWT auth endpoint check failed: invalid namespace");
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("JWT auth endpoint check succeeded");
|
||||||
|
} catch (InvalidJsonException e) {
|
||||||
|
log.warn("JWT auth endpoint check failed: Invalid JSON response: {}", e.getMessage());
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.warn("JWT auth endpoint check failed: Could not read API endpoint: {}", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return cfg.isEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected WordpressAuthData authenticate(String username, String password) {
|
||||||
|
JsonObject body = new JsonObject();
|
||||||
|
body.addProperty("username", username);
|
||||||
|
body.addProperty("password", password);
|
||||||
|
HttpPost req = RestClientUtils.post(jwtEndpoint + "/token", body);
|
||||||
|
try (CloseableHttpResponse res = client.execute(req)) {
|
||||||
|
int status = res.getStatusLine().getStatusCode();
|
||||||
|
String bodyRes = EntityUtils.toString(res.getEntity());
|
||||||
|
if (status != 200) {
|
||||||
|
throw new IllegalArgumentException(bodyRes);
|
||||||
|
}
|
||||||
|
|
||||||
|
return GsonUtil.get().fromJson(bodyRes, WordpressAuthData.class);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void authenticate() {
|
||||||
|
WordpressAuthData data = authenticate(
|
||||||
|
cfg.getRest().getCredential().getUsername(),
|
||||||
|
cfg.getRest().getCredential().getPassword());
|
||||||
|
log.info("Internal authentication: success, logged in as " + data.getUserNicename());
|
||||||
|
token = data.getToken();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected CloseableHttpResponse runRequest(HttpRequestBase request) throws IOException {
|
||||||
|
request.setHeader("Authorization", "Bearer " + token);
|
||||||
|
return client.execute(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CloseableHttpResponse withAuthentication(HttpRequestBase request) throws IOException {
|
||||||
|
CloseableHttpResponse response = runRequest(request);
|
||||||
|
if (response.getStatusLine().getStatusCode() == 403) { //FIXME we should check the JWT expiration time
|
||||||
|
authenticate();
|
||||||
|
response = runRequest(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,118 @@
|
|||||||
|
/*
|
||||||
|
* 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.wordpress;
|
||||||
|
|
||||||
|
import io.kamax.matrix.MatrixID;
|
||||||
|
import io.kamax.matrix.ThreePid;
|
||||||
|
import io.kamax.matrix._MatrixID;
|
||||||
|
import io.kamax.mxisd.config.MatrixConfig;
|
||||||
|
import io.kamax.mxisd.config.wordpress.WordpressConfig;
|
||||||
|
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.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class WordpressThreePidProvider implements IThreePidProvider {
|
||||||
|
|
||||||
|
private final Logger log = LoggerFactory.getLogger(WordpressThreePidProvider.class);
|
||||||
|
|
||||||
|
private MatrixConfig mxCfg;
|
||||||
|
private WordpressConfig cfg;
|
||||||
|
private WordressSqlBackend wordpress;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public WordpressThreePidProvider(MatrixConfig mxCfg, WordpressConfig cfg, WordressSqlBackend wordpress) {
|
||||||
|
this.mxCfg = mxCfg;
|
||||||
|
this.cfg = cfg;
|
||||||
|
this.wordpress = wordpress;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return wordpress.isEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isLocal() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getPriority() {
|
||||||
|
return 15;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Optional<_MatrixID> find(ThreePid tpid) {
|
||||||
|
String query = cfg.getSql().getQuery().getThreepid().get(tpid.getMedium());
|
||||||
|
if (Objects.isNull(query)) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
try (Connection conn = wordpress.getConnection()) {
|
||||||
|
PreparedStatement stmt = conn.prepareStatement(query);
|
||||||
|
stmt.setString(1, tpid.getAddress());
|
||||||
|
|
||||||
|
try (ResultSet rSet = stmt.executeQuery()) {
|
||||||
|
while (rSet.next()) {
|
||||||
|
String uid = rSet.getString("uid");
|
||||||
|
log.info("Found match: {}", uid);
|
||||||
|
try {
|
||||||
|
return Optional.of(MatrixID.from(uid, mxCfg.getDomain()).valid());
|
||||||
|
} catch (IllegalArgumentException ex) {
|
||||||
|
log.warn("Ignoring match {} - Invalid characters for a Matrix ID", uid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("No valid match found in Wordpress");
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<SingleLookupReply> find(SingleLookupRequest request) {
|
||||||
|
return find(new ThreePid(request.getType(), request.getThreePid())).map(mxid -> new SingleLookupReply(request, mxid));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ThreePidMapping> populate(List<ThreePidMapping> mappings) {
|
||||||
|
for (ThreePidMapping tpidMap : mappings) {
|
||||||
|
find(new ThreePid(tpidMap.getMedium(), tpidMap.getValue())).ifPresent(mxid -> tpidMap.setMxid(mxid.getId()));
|
||||||
|
}
|
||||||
|
return mappings;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* 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.wordpress;
|
||||||
|
|
||||||
|
import com.mchange.v2.c3p0.ComboPooledDataSource;
|
||||||
|
import io.kamax.mxisd.config.wordpress.WordpressConfig;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class WordressSqlBackend {
|
||||||
|
|
||||||
|
private Logger log = LoggerFactory.getLogger(WordressSqlBackend.class);
|
||||||
|
|
||||||
|
private WordpressConfig cfg;
|
||||||
|
|
||||||
|
private ComboPooledDataSource ds;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public WordressSqlBackend(WordpressConfig cfg) {
|
||||||
|
this.cfg = cfg;
|
||||||
|
|
||||||
|
ds = new ComboPooledDataSource();
|
||||||
|
ds.setJdbcUrl("jdbc:" + cfg.getSql().getType() + ":" + cfg.getSql().getConnection());
|
||||||
|
ds.setMinPoolSize(1);
|
||||||
|
ds.setMaxPoolSize(10);
|
||||||
|
ds.setAcquireIncrement(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return cfg.isEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Connection getConnection() throws SQLException {
|
||||||
|
return ds.getConnection();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -20,22 +20,18 @@
|
|||||||
|
|
||||||
package io.kamax.mxisd.config.ldap;
|
package io.kamax.mxisd.config.ldap;
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
|
||||||
import io.kamax.matrix.ThreePidMedium;
|
import io.kamax.matrix.ThreePidMedium;
|
||||||
|
import io.kamax.matrix.json.GsonUtil;
|
||||||
import io.kamax.mxisd.backend.ldap.LdapBackend;
|
import io.kamax.mxisd.backend.ldap.LdapBackend;
|
||||||
import io.kamax.mxisd.exception.ConfigurationException;
|
import io.kamax.mxisd.exception.ConfigurationException;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
@Configuration
|
public abstract class LdapConfig {
|
||||||
@ConfigurationProperties(prefix = "ldap")
|
|
||||||
public class LdapConfig {
|
|
||||||
|
|
||||||
public static class UID {
|
public static class UID {
|
||||||
|
|
||||||
@@ -240,7 +236,6 @@ public class LdapConfig {
|
|||||||
|
|
||||||
|
|
||||||
private Logger log = LoggerFactory.getLogger(LdapConfig.class);
|
private Logger log = LoggerFactory.getLogger(LdapConfig.class);
|
||||||
private static Gson gson = new Gson();
|
|
||||||
|
|
||||||
private boolean enabled;
|
private boolean enabled;
|
||||||
private String filter;
|
private String filter;
|
||||||
@@ -251,6 +246,8 @@ public class LdapConfig {
|
|||||||
private Directory directory;
|
private Directory directory;
|
||||||
private Identity identity;
|
private Identity identity;
|
||||||
|
|
||||||
|
protected abstract String getConfigName();
|
||||||
|
|
||||||
public boolean isEnabled() {
|
public boolean isEnabled() {
|
||||||
return enabled;
|
return enabled;
|
||||||
}
|
}
|
||||||
@@ -309,7 +306,7 @@ public class LdapConfig {
|
|||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
public void build() {
|
public void build() {
|
||||||
log.info("--- LDAP Config ---");
|
log.info("--- " + getConfigName() + " Config ---");
|
||||||
log.info("Enabled: {}", isEnabled());
|
log.info("Enabled: {}", isEnabled());
|
||||||
|
|
||||||
if (!isEnabled()) {
|
if (!isEnabled()) {
|
||||||
@@ -365,10 +362,10 @@ public class LdapConfig {
|
|||||||
log.info("Bind DN: {}", connection.getBindDn());
|
log.info("Bind DN: {}", connection.getBindDn());
|
||||||
log.info("Base DN: {}", connection.getBaseDn());
|
log.info("Base DN: {}", connection.getBaseDn());
|
||||||
|
|
||||||
log.info("Attribute: {}", gson.toJson(attribute));
|
log.info("Attribute: {}", GsonUtil.get().toJson(attribute));
|
||||||
log.info("Auth: {}", gson.toJson(auth));
|
log.info("Auth: {}", GsonUtil.get().toJson(auth));
|
||||||
log.info("Directory: {}", gson.toJson(directory));
|
log.info("Directory: {}", GsonUtil.get().toJson(directory));
|
||||||
log.info("Identity: {}", gson.toJson(identity));
|
log.info("Identity: {}", GsonUtil.get().toJson(identity));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -30,4 +30,9 @@ import org.springframework.context.annotation.Primary;
|
|||||||
@Primary
|
@Primary
|
||||||
public class GenericLdapConfig extends LdapConfig {
|
public class GenericLdapConfig extends LdapConfig {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getConfigName() {
|
||||||
|
return "Generic LDAP";
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -20,12 +20,17 @@
|
|||||||
|
|
||||||
package io.kamax.mxisd.config.ldap.netiq;
|
package io.kamax.mxisd.config.ldap.netiq;
|
||||||
|
|
||||||
import io.kamax.mxisd.config.ldap.generic.GenericLdapConfig;
|
import io.kamax.mxisd.config.ldap.LdapConfig;
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@ConfigurationProperties(prefix = "netiq")
|
@ConfigurationProperties(prefix = "netiq")
|
||||||
public class NetIqLdapConfig extends GenericLdapConfig {
|
public class NetIqLdapConfig extends LdapConfig {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getConfigName() {
|
||||||
|
return "NetIQ eDirectory";
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -31,6 +31,7 @@ public class MemoryIdentityConfig {
|
|||||||
private String username;
|
private String username;
|
||||||
private String password;
|
private String password;
|
||||||
private List<MemoryThreePid> threepids = new ArrayList<>();
|
private List<MemoryThreePid> threepids = new ArrayList<>();
|
||||||
|
private List<String> roles = new ArrayList<>();
|
||||||
|
|
||||||
public String getUsername() {
|
public String getUsername() {
|
||||||
return username;
|
return username;
|
||||||
@@ -56,4 +57,12 @@ public class MemoryIdentityConfig {
|
|||||||
this.threepids = threepids;
|
this.threepids = threepids;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<String> getRoles() {
|
||||||
|
return roles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRoles(List<String> roles) {
|
||||||
|
this.roles = roles;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -20,14 +20,16 @@
|
|||||||
|
|
||||||
package io.kamax.mxisd.config.memory;
|
package io.kamax.mxisd.config.memory;
|
||||||
|
|
||||||
|
import io.kamax.matrix._ThreePid;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class MemoryThreePid {
|
public class MemoryThreePid implements _ThreePid {
|
||||||
|
|
||||||
private String medium;
|
private String medium;
|
||||||
private String address;
|
private String address;
|
||||||
|
|
||||||
|
@Override
|
||||||
public String getMedium() {
|
public String getMedium() {
|
||||||
return medium;
|
return medium;
|
||||||
}
|
}
|
||||||
@@ -36,6 +38,7 @@ public class MemoryThreePid {
|
|||||||
this.medium = medium;
|
this.medium = medium;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public String getAddress() {
|
public String getAddress() {
|
||||||
return address;
|
return address;
|
||||||
}
|
}
|
||||||
|
@@ -37,22 +37,22 @@ public abstract class SqlConfig {
|
|||||||
|
|
||||||
public static class Type {
|
public static class Type {
|
||||||
|
|
||||||
private GenericSqlProviderConfig.Query name = new GenericSqlProviderConfig.Query();
|
private Query name = new Query();
|
||||||
private GenericSqlProviderConfig.Query threepid = new GenericSqlProviderConfig.Query();
|
private Query threepid = new Query();
|
||||||
|
|
||||||
public GenericSqlProviderConfig.Query getName() {
|
public Query getName() {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setName(GenericSqlProviderConfig.Query name) {
|
public void setName(Query name) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public GenericSqlProviderConfig.Query getThreepid() {
|
public Query getThreepid() {
|
||||||
return threepid;
|
return threepid;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setThreepid(GenericSqlProviderConfig.Query threepid) {
|
public void setThreepid(Query threepid) {
|
||||||
this.threepid = threepid;
|
this.threepid = threepid;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,7 +75,7 @@ public abstract class SqlConfig {
|
|||||||
public static class Directory {
|
public static class Directory {
|
||||||
|
|
||||||
private Boolean enabled;
|
private Boolean enabled;
|
||||||
private GenericSqlProviderConfig.Type query = new GenericSqlProviderConfig.Type();
|
private Type query = new Type();
|
||||||
|
|
||||||
public Boolean isEnabled() {
|
public Boolean isEnabled() {
|
||||||
return enabled;
|
return enabled;
|
||||||
@@ -85,11 +85,11 @@ public abstract class SqlConfig {
|
|||||||
this.enabled = enabled;
|
this.enabled = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
public GenericSqlProviderConfig.Type getQuery() {
|
public Type getQuery() {
|
||||||
return query;
|
return query;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setQuery(GenericSqlProviderConfig.Type query) {
|
public void setQuery(Type query) {
|
||||||
this.query = query;
|
this.query = query;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,12 +136,41 @@ public abstract class SqlConfig {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class ProfileThreepids {
|
||||||
|
|
||||||
|
private String query;
|
||||||
|
|
||||||
|
public String getQuery() {
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setQuery(String query) {
|
||||||
|
this.query = query;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Profile {
|
||||||
|
|
||||||
|
private ProfileThreepids threepid = new ProfileThreepids();
|
||||||
|
|
||||||
|
public ProfileThreepids getThreepid() {
|
||||||
|
return threepid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setThreepid(ProfileThreepids threepid) {
|
||||||
|
this.threepid = threepid;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private boolean enabled;
|
private boolean enabled;
|
||||||
private String type;
|
private String type;
|
||||||
private String connection;
|
private String connection;
|
||||||
private GenericSqlProviderConfig.Auth auth = new GenericSqlProviderConfig.Auth();
|
private Auth auth = new Auth();
|
||||||
private GenericSqlProviderConfig.Directory directory = new GenericSqlProviderConfig.Directory();
|
private Directory directory = new Directory();
|
||||||
private GenericSqlProviderConfig.Identity identity = new GenericSqlProviderConfig.Identity();
|
private Identity identity = new Identity();
|
||||||
|
private Profile profile = new Profile();
|
||||||
|
|
||||||
public boolean isEnabled() {
|
public boolean isEnabled() {
|
||||||
return enabled;
|
return enabled;
|
||||||
@@ -167,30 +196,38 @@ public abstract class SqlConfig {
|
|||||||
this.connection = connection;
|
this.connection = connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
public GenericSqlProviderConfig.Auth getAuth() {
|
public Auth getAuth() {
|
||||||
return auth;
|
return auth;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setAuth(GenericSqlProviderConfig.Auth auth) {
|
public void setAuth(Auth auth) {
|
||||||
this.auth = auth;
|
this.auth = auth;
|
||||||
}
|
}
|
||||||
|
|
||||||
public GenericSqlProviderConfig.Directory getDirectory() {
|
public Directory getDirectory() {
|
||||||
return directory;
|
return directory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDirectory(GenericSqlProviderConfig.Directory directory) {
|
public void setDirectory(Directory directory) {
|
||||||
this.directory = directory;
|
this.directory = directory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public GenericSqlProviderConfig.Identity getIdentity() {
|
public Identity getIdentity() {
|
||||||
return identity;
|
return identity;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setIdentity(GenericSqlProviderConfig.Identity identity) {
|
public void setIdentity(Identity identity) {
|
||||||
this.identity = identity;
|
this.identity = identity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Profile getProfile() {
|
||||||
|
return profile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProfile(Profile profile) {
|
||||||
|
this.profile = profile;
|
||||||
|
}
|
||||||
|
|
||||||
protected abstract String getProviderName();
|
protected abstract String getProviderName();
|
||||||
|
|
||||||
protected void doBuild() {
|
protected void doBuild() {
|
||||||
@@ -222,6 +259,7 @@ public abstract class SqlConfig {
|
|||||||
log.info("Identity type: {}", getIdentity().getType());
|
log.info("Identity type: {}", getIdentity().getType());
|
||||||
log.info("3PID mapping query: {}", getIdentity().getQuery());
|
log.info("3PID mapping query: {}", getIdentity().getQuery());
|
||||||
log.info("Identity medium queries: {}", GsonUtil.build().toJson(getIdentity().getMedium()));
|
log.info("Identity medium queries: {}", GsonUtil.build().toJson(getIdentity().getMedium()));
|
||||||
|
log.info("Profile 3PID query: {}", getProfile().getThreepid().getQuery());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -0,0 +1,175 @@
|
|||||||
|
/*
|
||||||
|
* 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.wordpress;
|
||||||
|
|
||||||
|
import io.kamax.mxisd.exception.ConfigurationException;
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
import javax.annotation.PostConstruct;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@ConfigurationProperties("wordpress")
|
||||||
|
public class WordpressConfig {
|
||||||
|
|
||||||
|
public static class Credential {
|
||||||
|
|
||||||
|
private String username;
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
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 static class Rest {
|
||||||
|
|
||||||
|
private Credential credential = new Credential();
|
||||||
|
private String base;
|
||||||
|
|
||||||
|
public String getBase() {
|
||||||
|
return base;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBase(String base) {
|
||||||
|
this.base = base;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Credential getCredential() {
|
||||||
|
return credential;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCredential(Credential credential) {
|
||||||
|
this.credential = credential;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Query {
|
||||||
|
|
||||||
|
private Map<String, String> threepid;
|
||||||
|
private Map<String, String> directory;
|
||||||
|
|
||||||
|
public Map<String, String> getThreepid() {
|
||||||
|
return threepid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setThreepid(Map<String, String> threepid) {
|
||||||
|
this.threepid = threepid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getDirectory() {
|
||||||
|
return directory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDirectory(Map<String, String> directory) {
|
||||||
|
this.directory = directory;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Sql {
|
||||||
|
|
||||||
|
private String type;
|
||||||
|
private String connection;
|
||||||
|
private Query query;
|
||||||
|
|
||||||
|
public String getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setType(String type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getConnection() {
|
||||||
|
return connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setConnection(String connection) {
|
||||||
|
this.connection = connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Query getQuery() {
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setQuery(Query query) {
|
||||||
|
this.query = query;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean enabled;
|
||||||
|
private Rest rest = new Rest();
|
||||||
|
private Sql sql = new Sql();
|
||||||
|
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnabled(boolean enabled) {
|
||||||
|
this.enabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rest getRest() {
|
||||||
|
return rest;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRest(Rest rest) {
|
||||||
|
this.rest = rest;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Sql getSql() {
|
||||||
|
return sql;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSql(Sql sql) {
|
||||||
|
this.sql = sql;
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
public void build() {
|
||||||
|
if (!isEnabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringUtils.isBlank(getRest().getBase())) {
|
||||||
|
throw new ConfigurationException("wordpress.rest.base");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -84,8 +84,8 @@ public class DefaultExceptionHandler {
|
|||||||
return handleGeneric(request, response, e);
|
return handleGeneric(request, response, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ExceptionHandler(MatrixException.class)
|
@ExceptionHandler(HttpMatrixException.class)
|
||||||
public String handleGeneric(HttpServletRequest request, HttpServletResponse response, MatrixException e) {
|
public String handleGeneric(HttpServletRequest request, HttpServletResponse response, HttpMatrixException e) {
|
||||||
response.setStatus(e.getStatus());
|
response.setStatus(e.getStatus());
|
||||||
return handle(request, e.getErrorCode(), e.getError());
|
return handle(request, e.getErrorCode(), e.getError());
|
||||||
}
|
}
|
||||||
|
54
src/main/java/io/kamax/mxisd/controller/ProxyController.java
Normal file
54
src/main/java/io/kamax/mxisd/controller/ProxyController.java
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* mxisd - Matrix Identity Server Daemon
|
||||||
|
* Copyright (C) 2018 Kamax Sarl
|
||||||
|
*
|
||||||
|
* https://www.kamax.io/
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.kamax.mxisd.controller;
|
||||||
|
|
||||||
|
import io.kamax.mxisd.exception.AccessTokenNotFoundException;
|
||||||
|
import io.kamax.mxisd.util.OptionalUtil;
|
||||||
|
import org.thymeleaf.util.StringUtils;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public class ProxyController {
|
||||||
|
|
||||||
|
private final static String headerName = "Authorization";
|
||||||
|
private final static String headerValuePrefix = "Bearer ";
|
||||||
|
private final static String parameterName = "access_token";
|
||||||
|
|
||||||
|
Optional<String> findAccessTokenInHeaders(HttpServletRequest request) {
|
||||||
|
return Optional.ofNullable(request.getHeader(headerName))
|
||||||
|
.filter(header -> StringUtils.startsWith(header, headerValuePrefix))
|
||||||
|
.map(header -> header.substring(headerValuePrefix.length()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Optional<String> findAccessTokenInQuery(HttpServletRequest request) {
|
||||||
|
return Optional.ofNullable(request.getParameter(parameterName));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<String> findAccessToken(HttpServletRequest request) {
|
||||||
|
return OptionalUtil.findFirst(() -> findAccessTokenInHeaders(request), () -> findAccessTokenInQuery(request));
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAccessToken(HttpServletRequest request) {
|
||||||
|
return findAccessToken(request).orElseThrow(AccessTokenNotFoundException::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
package io.kamax.mxisd.controller.auth.v1.io;
|
package io.kamax.mxisd.controller.auth.v1.io;
|
||||||
|
|
||||||
import io.kamax.mxisd.ThreePid;
|
import io.kamax.matrix.ThreePid;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@@ -21,6 +21,7 @@
|
|||||||
package io.kamax.mxisd.controller.directory.v1;
|
package io.kamax.mxisd.controller.directory.v1;
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
|
import io.kamax.mxisd.controller.ProxyController;
|
||||||
import io.kamax.mxisd.controller.directory.v1.io.UserDirectorySearchRequest;
|
import io.kamax.mxisd.controller.directory.v1.io.UserDirectorySearchRequest;
|
||||||
import io.kamax.mxisd.controller.directory.v1.io.UserDirectorySearchResult;
|
import io.kamax.mxisd.controller.directory.v1.io.UserDirectorySearchResult;
|
||||||
import io.kamax.mxisd.directory.DirectoryManager;
|
import io.kamax.mxisd.directory.DirectoryManager;
|
||||||
@@ -28,7 +29,10 @@ import io.kamax.mxisd.util.GsonParser;
|
|||||||
import io.kamax.mxisd.util.GsonUtil;
|
import io.kamax.mxisd.util.GsonUtil;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.CrossOrigin;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMethod;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -37,7 +41,7 @@ import java.net.URI;
|
|||||||
@RestController
|
@RestController
|
||||||
@CrossOrigin
|
@CrossOrigin
|
||||||
@RequestMapping(path = "/_matrix/client/r0/user_directory", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
|
@RequestMapping(path = "/_matrix/client/r0/user_directory", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
|
||||||
public class UserDirectoryController {
|
public class UserDirectoryController extends ProxyController {
|
||||||
|
|
||||||
private Gson gson = GsonUtil.build();
|
private Gson gson = GsonUtil.build();
|
||||||
private GsonParser parser = new GsonParser(gson);
|
private GsonParser parser = new GsonParser(gson);
|
||||||
@@ -46,7 +50,8 @@ public class UserDirectoryController {
|
|||||||
private DirectoryManager mgr;
|
private DirectoryManager mgr;
|
||||||
|
|
||||||
@RequestMapping(path = "/search", method = RequestMethod.POST)
|
@RequestMapping(path = "/search", method = RequestMethod.POST)
|
||||||
public String search(HttpServletRequest request, @RequestParam("access_token") String accessToken) throws IOException {
|
public String search(HttpServletRequest request) throws IOException {
|
||||||
|
String accessToken = getAccessToken(request);
|
||||||
UserDirectorySearchRequest searchQuery = parser.parse(request, UserDirectorySearchRequest.class);
|
UserDirectorySearchRequest searchQuery = parser.parse(request, UserDirectorySearchRequest.class);
|
||||||
URI target = URI.create(request.getRequestURL().toString());
|
URI target = URI.create(request.getRequestURL().toString());
|
||||||
UserDirectorySearchResult result = mgr.search(target, accessToken, searchQuery.getSearchTerm());
|
UserDirectorySearchResult result = mgr.search(target, accessToken, searchQuery.getSearchTerm());
|
||||||
|
@@ -22,13 +22,13 @@ package io.kamax.mxisd.controller.identity.v1;
|
|||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import io.kamax.matrix.MatrixID;
|
import io.kamax.matrix.MatrixID;
|
||||||
|
import io.kamax.matrix.crypto.KeyManager;
|
||||||
import io.kamax.mxisd.config.ServerConfig;
|
import io.kamax.mxisd.config.ServerConfig;
|
||||||
import io.kamax.mxisd.controller.identity.v1.io.ThreePidInviteReplyIO;
|
import io.kamax.mxisd.controller.identity.v1.io.ThreePidInviteReplyIO;
|
||||||
import io.kamax.mxisd.invitation.IThreePidInvite;
|
import io.kamax.mxisd.invitation.IThreePidInvite;
|
||||||
import io.kamax.mxisd.invitation.IThreePidInviteReply;
|
import io.kamax.mxisd.invitation.IThreePidInviteReply;
|
||||||
import io.kamax.mxisd.invitation.InvitationManager;
|
import io.kamax.mxisd.invitation.InvitationManager;
|
||||||
import io.kamax.mxisd.invitation.ThreePidInvite;
|
import io.kamax.mxisd.invitation.ThreePidInvite;
|
||||||
import io.kamax.mxisd.key.KeyManager;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
@@ -22,9 +22,9 @@ package io.kamax.mxisd.controller.identity.v1;
|
|||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
|
import io.kamax.matrix.crypto.KeyManager;
|
||||||
import io.kamax.mxisd.controller.identity.v1.io.KeyValidityJson;
|
import io.kamax.mxisd.controller.identity.v1.io.KeyValidityJson;
|
||||||
import io.kamax.mxisd.exception.BadRequestException;
|
import io.kamax.mxisd.exception.BadRequestException;
|
||||||
import io.kamax.mxisd.key.KeyManager;
|
|
||||||
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;
|
||||||
@@ -64,7 +64,7 @@ public class KeyController {
|
|||||||
|
|
||||||
@RequestMapping(value = "/pubkey/ephemeral/isvalid", method = GET)
|
@RequestMapping(value = "/pubkey/ephemeral/isvalid", method = GET)
|
||||||
public String checkEphemeralKeyValidity(HttpServletRequest request) {
|
public String checkEphemeralKeyValidity(HttpServletRequest request) {
|
||||||
log.warn("Ephemeral key was request but no ephemeral key are generated, replying not valid");
|
log.warn("Ephemeral key was requested but no ephemeral key are generated, replying not valid");
|
||||||
|
|
||||||
return invalidKey;
|
return invalidKey;
|
||||||
}
|
}
|
||||||
|
@@ -22,11 +22,14 @@ package io.kamax.mxisd.controller.identity.v1;
|
|||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
|
import io.kamax.matrix.crypto.SignatureManager;
|
||||||
|
import io.kamax.matrix.event.EventKey;
|
||||||
|
import io.kamax.matrix.json.MatrixJson;
|
||||||
|
import io.kamax.mxisd.config.MatrixConfig;
|
||||||
import io.kamax.mxisd.controller.identity.v1.io.SingeLookupReplyJson;
|
import io.kamax.mxisd.controller.identity.v1.io.SingeLookupReplyJson;
|
||||||
import io.kamax.mxisd.exception.InternalServerError;
|
import io.kamax.mxisd.exception.InternalServerError;
|
||||||
import io.kamax.mxisd.lookup.*;
|
import io.kamax.mxisd.lookup.*;
|
||||||
import io.kamax.mxisd.lookup.strategy.LookupStrategy;
|
import io.kamax.mxisd.lookup.strategy.LookupStrategy;
|
||||||
import io.kamax.mxisd.signature.SignatureManager;
|
|
||||||
import io.kamax.mxisd.util.GsonParser;
|
import io.kamax.mxisd.util.GsonParser;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@@ -57,6 +60,9 @@ public class MappingController {
|
|||||||
private Gson gson = new Gson();
|
private Gson gson = new Gson();
|
||||||
private GsonParser parser = new GsonParser(gson);
|
private GsonParser parser = new GsonParser(gson);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private MatrixConfig mxCfg;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private LookupStrategy strategy;
|
private LookupStrategy strategy;
|
||||||
|
|
||||||
@@ -92,16 +98,12 @@ public class MappingController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SingleLookupReply lookup = lookupOpt.get();
|
SingleLookupReply lookup = lookupOpt.get();
|
||||||
if (lookup.isSigned()) {
|
|
||||||
log.info("Lookup is already signed, sending as-is");
|
|
||||||
return lookup.getBody();
|
|
||||||
} else {
|
|
||||||
log.info("Lookup is not signed, signing");
|
|
||||||
JsonObject obj = gson.toJsonTree(new SingeLookupReplyJson(lookup)).getAsJsonObject();
|
|
||||||
obj.add("signatures", signMgr.signMessageGson(gson.toJson(obj)));
|
|
||||||
|
|
||||||
return gson.toJson(obj);
|
// FIXME signing should be done in the business model, not in the controller
|
||||||
}
|
JsonObject obj = gson.toJsonTree(new SingeLookupReplyJson(lookup)).getAsJsonObject();
|
||||||
|
obj.add(EventKey.Signatures.get(), signMgr.signMessageGson(MatrixJson.encodeCanonical(obj)));
|
||||||
|
|
||||||
|
return gson.toJson(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequestMapping(value = "/bulk_lookup", method = POST)
|
@RequestMapping(value = "/bulk_lookup", method = POST)
|
||||||
|
@@ -22,8 +22,8 @@ package io.kamax.mxisd.controller.identity.v1;
|
|||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
|
import io.kamax.matrix.ThreePid;
|
||||||
import io.kamax.matrix.ThreePidMedium;
|
import io.kamax.matrix.ThreePidMedium;
|
||||||
import io.kamax.mxisd.ThreePid;
|
|
||||||
import io.kamax.mxisd.config.ServerConfig;
|
import io.kamax.mxisd.config.ServerConfig;
|
||||||
import io.kamax.mxisd.config.ViewConfig;
|
import io.kamax.mxisd.config.ViewConfig;
|
||||||
import io.kamax.mxisd.controller.identity.v1.io.SessionEmailTokenRequestJson;
|
import io.kamax.mxisd.controller.identity.v1.io.SessionEmailTokenRequestJson;
|
||||||
|
@@ -22,9 +22,6 @@ package io.kamax.mxisd.controller.identity.v1.io;
|
|||||||
|
|
||||||
import io.kamax.mxisd.lookup.SingleLookupReply;
|
import io.kamax.mxisd.lookup.SingleLookupReply;
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public class SingeLookupReplyJson {
|
public class SingeLookupReplyJson {
|
||||||
|
|
||||||
private String address;
|
private String address;
|
||||||
@@ -33,7 +30,6 @@ public class SingeLookupReplyJson {
|
|||||||
private long not_after;
|
private long not_after;
|
||||||
private long not_before;
|
private long not_before;
|
||||||
private long ts;
|
private long ts;
|
||||||
private Map<String, Map<String, String>> signatures = new HashMap<>();
|
|
||||||
|
|
||||||
public SingeLookupReplyJson(SingleLookupReply reply) {
|
public SingeLookupReplyJson(SingleLookupReply reply) {
|
||||||
this.address = reply.getRequest().getThreePid();
|
this.address = reply.getRequest().getThreePid();
|
||||||
@@ -68,8 +64,4 @@ public class SingeLookupReplyJson {
|
|||||||
return ts;
|
return ts;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isSigned() {
|
|
||||||
return signatures != null && !signatures.isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,105 @@
|
|||||||
|
/*
|
||||||
|
* 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.controller.profile.v1;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonParser;
|
||||||
|
import io.kamax.matrix.MatrixID;
|
||||||
|
import io.kamax.matrix._ThreePid;
|
||||||
|
import io.kamax.mxisd.controller.ProxyController;
|
||||||
|
import io.kamax.mxisd.dns.ClientDnsOverwrite;
|
||||||
|
import io.kamax.mxisd.profile.ProfileManager;
|
||||||
|
import io.kamax.mxisd.util.GsonUtil;
|
||||||
|
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||||
|
import org.apache.http.client.methods.HttpGet;
|
||||||
|
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;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.web.bind.annotation.CrossOrigin;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
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;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@CrossOrigin
|
||||||
|
@RequestMapping(path = "/_matrix/client/r0/profile", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
|
||||||
|
public class ProfileController extends ProxyController {
|
||||||
|
|
||||||
|
private final Logger log = LoggerFactory.getLogger(ProfileController.class);
|
||||||
|
private final ProfileManager mgr;
|
||||||
|
private final CloseableHttpClient client;
|
||||||
|
private final ClientDnsOverwrite dns;
|
||||||
|
private final JsonParser parser;
|
||||||
|
private final Gson gson;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public ProfileController(ProfileManager mgr, CloseableHttpClient client, ClientDnsOverwrite dns) {
|
||||||
|
this.mgr = mgr;
|
||||||
|
this.client = client;
|
||||||
|
this.dns = dns;
|
||||||
|
this.parser = new JsonParser();
|
||||||
|
this.gson = GsonUtil.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME do properly in the SDK (headers, check access token, etc.)
|
||||||
|
private String resolveProxyUrl(HttpServletRequest req) {
|
||||||
|
URI target = URI.create(req.getRequestURL().toString() + (Objects.isNull(req.getQueryString()) ? "" : "?" + req.getQueryString()));
|
||||||
|
URIBuilder builder = dns.transform(target);
|
||||||
|
String urlToLogin = builder.toString();
|
||||||
|
log.info("Proxy resolution: {} to {}", target.toString(), urlToLogin);
|
||||||
|
return urlToLogin;
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping("/{userId:.+}")
|
||||||
|
public String getProfile(HttpServletRequest req, HttpServletResponse res, @PathVariable String userId) {
|
||||||
|
Optional<String> accessTokenOpt = findAccessToken(req);
|
||||||
|
HttpGet reqOut = new HttpGet(resolveProxyUrl(req));
|
||||||
|
accessTokenOpt.ifPresent(accessToken -> reqOut.addHeader("Authorization", "Bearer " + accessToken));
|
||||||
|
|
||||||
|
try (CloseableHttpResponse hsResponse = client.execute(reqOut)) {
|
||||||
|
res.setStatus(hsResponse.getStatusLine().getStatusCode());
|
||||||
|
JsonElement el = parser.parse(EntityUtils.toString(hsResponse.getEntity()));
|
||||||
|
List<_ThreePid> list = mgr.getThreepids(MatrixID.asAcceptable(userId));
|
||||||
|
if (!list.isEmpty() && el.isJsonObject()) {
|
||||||
|
JsonObject obj = el.getAsJsonObject();
|
||||||
|
obj.add("threepids", GsonUtil.build().toJsonTree(list));
|
||||||
|
}
|
||||||
|
return gson.toJson(el);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* mxisd - Matrix Identity Server Daemon
|
||||||
|
* Copyright (C) 2018 Kamax Sarl
|
||||||
|
*
|
||||||
|
* https://www.kamax.io/
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.kamax.mxisd.controller.profile.v1;
|
||||||
|
|
||||||
|
import io.kamax.matrix.MatrixID;
|
||||||
|
import io.kamax.matrix._MatrixID;
|
||||||
|
import io.kamax.matrix.json.GsonUtil;
|
||||||
|
import io.kamax.mxisd.profile.ProfileManager;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.web.bind.annotation.CrossOrigin;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.net.URLDecoder;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
import static org.springframework.web.bind.annotation.RequestMethod.GET;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@CrossOrigin
|
||||||
|
@RequestMapping(produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
|
||||||
|
public class ProfileInternalController {
|
||||||
|
|
||||||
|
private final ProfileManager mgr;
|
||||||
|
|
||||||
|
public ProfileInternalController(ProfileManager mgr) {
|
||||||
|
this.mgr = mgr;
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping(method = GET, path = "/_matrix-internal/profile/v1/{userId:.+}")
|
||||||
|
public String getProfile(@PathVariable String userId) throws UnsupportedEncodingException {
|
||||||
|
userId = URLDecoder.decode(userId, StandardCharsets.UTF_8.name());
|
||||||
|
_MatrixID mxId = MatrixID.asAcceptable(userId);
|
||||||
|
|
||||||
|
return GsonUtil.get().toJson(GsonUtil.makeObj("roles", GsonUtil.asArray(mgr.getRoles(mxId))));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -27,8 +27,8 @@ import io.kamax.mxisd.config.DirectoryConfig;
|
|||||||
import io.kamax.mxisd.controller.directory.v1.io.UserDirectorySearchRequest;
|
import io.kamax.mxisd.controller.directory.v1.io.UserDirectorySearchRequest;
|
||||||
import io.kamax.mxisd.controller.directory.v1.io.UserDirectorySearchResult;
|
import io.kamax.mxisd.controller.directory.v1.io.UserDirectorySearchResult;
|
||||||
import io.kamax.mxisd.dns.ClientDnsOverwrite;
|
import io.kamax.mxisd.dns.ClientDnsOverwrite;
|
||||||
|
import io.kamax.mxisd.exception.HttpMatrixException;
|
||||||
import io.kamax.mxisd.exception.InternalServerError;
|
import io.kamax.mxisd.exception.InternalServerError;
|
||||||
import io.kamax.mxisd.exception.MatrixException;
|
|
||||||
import io.kamax.mxisd.util.GsonUtil;
|
import io.kamax.mxisd.util.GsonUtil;
|
||||||
import io.kamax.mxisd.util.RestClientUtils;
|
import io.kamax.mxisd.util.RestClientUtils;
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
@@ -99,7 +99,7 @@ public class DirectoryManager {
|
|||||||
log.warn("Homeserver does not support Directory feature, skipping");
|
log.warn("Homeserver does not support Directory feature, skipping");
|
||||||
} else {
|
} else {
|
||||||
log.error("Homeserver returned an error while performing directory search");
|
log.error("Homeserver returned an error while performing directory search");
|
||||||
throw new MatrixException(status, info.getErrcode(), info.getError());
|
throw new HttpMatrixException(status, info.getErrcode(), info.getError());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -22,6 +22,7 @@ package io.kamax.mxisd.dns;
|
|||||||
|
|
||||||
import io.kamax.mxisd.config.DnsOverwriteConfig;
|
import io.kamax.mxisd.config.DnsOverwriteConfig;
|
||||||
import io.kamax.mxisd.exception.ConfigurationException;
|
import io.kamax.mxisd.exception.ConfigurationException;
|
||||||
|
import io.kamax.mxisd.exception.InternalServerError;
|
||||||
import org.apache.http.client.utils.URIBuilder;
|
import org.apache.http.client.utils.URIBuilder;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -53,7 +54,7 @@ public class ClientDnsOverwrite {
|
|||||||
URIBuilder builder = new URIBuilder(initial);
|
URIBuilder builder = new URIBuilder(initial);
|
||||||
Entry mapping = mappings.get(initial.getHost());
|
Entry mapping = mappings.get(initial.getHost());
|
||||||
if (mapping == null) {
|
if (mapping == null) {
|
||||||
return builder;
|
throw new InternalServerError("No DNS client override for " + initial.getHost());
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* mxisd - Matrix Identity Server Daemon
|
||||||
|
* Copyright (C) 2018 Kamax Sarl
|
||||||
|
*
|
||||||
|
* https://www.kamax.io/
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.kamax.mxisd.exception;
|
||||||
|
|
||||||
|
public class AccessTokenNotFoundException extends HttpMatrixException {
|
||||||
|
|
||||||
|
public AccessTokenNotFoundException() {
|
||||||
|
super(401, "M_UNKNOWN_TOKEN", "An access token is required to access this resource");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -22,7 +22,7 @@ package io.kamax.mxisd.exception;
|
|||||||
|
|
||||||
import org.apache.http.HttpStatus;
|
import org.apache.http.HttpStatus;
|
||||||
|
|
||||||
public class FeatureNotAvailable extends MatrixException {
|
public class FeatureNotAvailable extends HttpMatrixException {
|
||||||
|
|
||||||
private String internalReason;
|
private String internalReason;
|
||||||
|
|
||||||
|
@@ -20,28 +20,19 @@
|
|||||||
|
|
||||||
package io.kamax.mxisd.exception;
|
package io.kamax.mxisd.exception;
|
||||||
|
|
||||||
public class MatrixException extends MxisdException {
|
import io.kamax.matrix.MatrixException;
|
||||||
|
|
||||||
|
public class HttpMatrixException extends MatrixException {
|
||||||
|
|
||||||
private int status;
|
private int status;
|
||||||
private String errorCode;
|
|
||||||
private String error;
|
|
||||||
|
|
||||||
public MatrixException(int status, String errorCode, String error) {
|
public HttpMatrixException(int status, String errorCode, String error) {
|
||||||
|
super(errorCode, error);
|
||||||
this.status = status;
|
this.status = status;
|
||||||
this.errorCode = errorCode;
|
|
||||||
this.error = error;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getStatus() {
|
public int getStatus() {
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getErrorCode() {
|
|
||||||
return errorCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getError() {
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
@@ -24,7 +24,7 @@ import org.apache.http.HttpStatus;
|
|||||||
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
|
||||||
public class InternalServerError extends MatrixException {
|
public class InternalServerError extends HttpMatrixException {
|
||||||
|
|
||||||
private String reference = Long.toString(Instant.now().toEpochMilli());
|
private String reference = Long.toString(Instant.now().toEpochMilli());
|
||||||
private String internalReason;
|
private String internalReason;
|
||||||
|
@@ -22,7 +22,7 @@ package io.kamax.mxisd.exception;
|
|||||||
|
|
||||||
import org.apache.http.HttpStatus;
|
import org.apache.http.HttpStatus;
|
||||||
|
|
||||||
public class MessageForClientException extends MatrixException {
|
public class MessageForClientException extends HttpMatrixException {
|
||||||
|
|
||||||
public MessageForClientException(String error) {
|
public MessageForClientException(String error) {
|
||||||
super(HttpStatus.SC_OK, "M_MESSAGE_FOR_CLIENT", error);
|
super(HttpStatus.SC_OK, "M_MESSAGE_FOR_CLIENT", error);
|
||||||
|
@@ -23,7 +23,7 @@ package io.kamax.mxisd.exception;
|
|||||||
|
|
||||||
import org.apache.http.HttpStatus;
|
import org.apache.http.HttpStatus;
|
||||||
|
|
||||||
public class NotAllowedException extends MatrixException {
|
public class NotAllowedException extends HttpMatrixException {
|
||||||
|
|
||||||
public NotAllowedException(String s) {
|
public NotAllowedException(String s) {
|
||||||
super(HttpStatus.SC_FORBIDDEN, "M_FORBIDDEN", s);
|
super(HttpStatus.SC_FORBIDDEN, "M_FORBIDDEN", s);
|
||||||
|
@@ -2,7 +2,7 @@ package io.kamax.mxisd.exception;
|
|||||||
|
|
||||||
import org.apache.http.HttpStatus;
|
import org.apache.http.HttpStatus;
|
||||||
|
|
||||||
public class RemoteHomeServerException extends MatrixException {
|
public class RemoteHomeServerException extends HttpMatrixException {
|
||||||
|
|
||||||
public RemoteHomeServerException(String error) {
|
public RemoteHomeServerException(String error) {
|
||||||
super(HttpStatus.SC_SERVICE_UNAVAILABLE, "M_REMOTE_HS_ERROR", "Error from remote server: " + error);
|
super(HttpStatus.SC_SERVICE_UNAVAILABLE, "M_REMOTE_HS_ERROR", "Error from remote server: " + error);
|
||||||
|
@@ -22,7 +22,7 @@ package io.kamax.mxisd.exception;
|
|||||||
|
|
||||||
import org.apache.http.HttpStatus;
|
import org.apache.http.HttpStatus;
|
||||||
|
|
||||||
public class RemoteIdentityServerException extends MatrixException {
|
public class RemoteIdentityServerException extends HttpMatrixException {
|
||||||
|
|
||||||
public RemoteIdentityServerException(String error) {
|
public RemoteIdentityServerException(String error) {
|
||||||
super(HttpStatus.SC_SERVICE_UNAVAILABLE, "M_REMOTE_IS_ERROR", "Error from remote server: " + error);
|
super(HttpStatus.SC_SERVICE_UNAVAILABLE, "M_REMOTE_IS_ERROR", "Error from remote server: " + error);
|
||||||
|
@@ -23,7 +23,7 @@ package io.kamax.mxisd.exception;
|
|||||||
|
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
|
|
||||||
public class RemoteLoginException extends MatrixException {
|
public class RemoteLoginException extends HttpMatrixException {
|
||||||
|
|
||||||
private JsonObject errorBodyMsgResp;
|
private JsonObject errorBodyMsgResp;
|
||||||
|
|
||||||
|
@@ -22,7 +22,7 @@ package io.kamax.mxisd.exception;
|
|||||||
|
|
||||||
import org.apache.http.HttpStatus;
|
import org.apache.http.HttpStatus;
|
||||||
|
|
||||||
public class SessionNotValidatedException extends MatrixException {
|
public class SessionNotValidatedException extends HttpMatrixException {
|
||||||
|
|
||||||
public SessionNotValidatedException() {
|
public SessionNotValidatedException() {
|
||||||
super(HttpStatus.SC_OK, "M_SESSION_NOT_VALIDATED", "This validation session has not yet been completed");
|
super(HttpStatus.SC_OK, "M_SESSION_NOT_VALIDATED", "This validation session has not yet been completed");
|
||||||
|
@@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
package io.kamax.mxisd.exception;
|
package io.kamax.mxisd.exception;
|
||||||
|
|
||||||
public class SessionUnknownException extends MatrixException {
|
public class SessionUnknownException extends HttpMatrixException {
|
||||||
|
|
||||||
public SessionUnknownException() {
|
public SessionUnknownException() {
|
||||||
this("No valid session was found matching that sid and client secret");
|
this("No valid session was found matching that sid and client secret");
|
||||||
|
@@ -24,6 +24,7 @@ import com.google.gson.Gson;
|
|||||||
import com.google.gson.JsonArray;
|
import com.google.gson.JsonArray;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import io.kamax.matrix.MatrixID;
|
import io.kamax.matrix.MatrixID;
|
||||||
|
import io.kamax.matrix.crypto.SignatureManager;
|
||||||
import io.kamax.mxisd.config.InvitationConfig;
|
import io.kamax.mxisd.config.InvitationConfig;
|
||||||
import io.kamax.mxisd.dns.FederationDnsOverwrite;
|
import io.kamax.mxisd.dns.FederationDnsOverwrite;
|
||||||
import io.kamax.mxisd.exception.BadRequestException;
|
import io.kamax.mxisd.exception.BadRequestException;
|
||||||
@@ -32,7 +33,6 @@ import io.kamax.mxisd.lookup.SingleLookupReply;
|
|||||||
import io.kamax.mxisd.lookup.ThreePidMapping;
|
import io.kamax.mxisd.lookup.ThreePidMapping;
|
||||||
import io.kamax.mxisd.lookup.strategy.LookupStrategy;
|
import io.kamax.mxisd.lookup.strategy.LookupStrategy;
|
||||||
import io.kamax.mxisd.notification.NotificationManager;
|
import io.kamax.mxisd.notification.NotificationManager;
|
||||||
import io.kamax.mxisd.signature.SignatureManager;
|
|
||||||
import io.kamax.mxisd.storage.IStorage;
|
import io.kamax.mxisd.storage.IStorage;
|
||||||
import io.kamax.mxisd.storage.ormlite.ThreePidInviteIO;
|
import io.kamax.mxisd.storage.ormlite.ThreePidInviteIO;
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
|
@@ -1,115 +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.key;
|
|
||||||
|
|
||||||
import io.kamax.mxisd.config.KeyConfig;
|
|
||||||
import net.i2p.crypto.eddsa.EdDSAEngine;
|
|
||||||
import net.i2p.crypto.eddsa.EdDSAPrivateKey;
|
|
||||||
import net.i2p.crypto.eddsa.EdDSAPublicKey;
|
|
||||||
import net.i2p.crypto.eddsa.KeyPairGenerator;
|
|
||||||
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable;
|
|
||||||
import net.i2p.crypto.eddsa.spec.EdDSAParameterSpec;
|
|
||||||
import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec;
|
|
||||||
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;
|
|
||||||
import org.apache.commons.io.FileUtils;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.nio.file.Paths;
|
|
||||||
import java.security.KeyPair;
|
|
||||||
import java.security.MessageDigest;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.security.PrivateKey;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Base64;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@Component
|
|
||||||
public class KeyManager {
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private KeyConfig keyCfg;
|
|
||||||
|
|
||||||
private EdDSAParameterSpec keySpecs;
|
|
||||||
private EdDSAEngine signEngine;
|
|
||||||
private List<KeyPair> keys;
|
|
||||||
|
|
||||||
@PostConstruct
|
|
||||||
public void build() {
|
|
||||||
try {
|
|
||||||
keySpecs = EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.CURVE_ED25519_SHA512);
|
|
||||||
signEngine = new EdDSAEngine(MessageDigest.getInstance(keySpecs.getHashAlgorithm()));
|
|
||||||
keys = new ArrayList<>();
|
|
||||||
|
|
||||||
Path privKey = Paths.get(keyCfg.getPath());
|
|
||||||
|
|
||||||
if (!Files.exists(privKey)) {
|
|
||||||
KeyPair pair = (new KeyPairGenerator()).generateKeyPair();
|
|
||||||
String keyEncoded = Base64.getEncoder().encodeToString(pair.getPrivate().getEncoded());
|
|
||||||
FileUtils.writeStringToFile(privKey.toFile(), keyEncoded, StandardCharsets.ISO_8859_1);
|
|
||||||
keys.add(pair);
|
|
||||||
} else {
|
|
||||||
if (Files.isDirectory(privKey)) {
|
|
||||||
throw new RuntimeException("Invalid path for private key: " + privKey.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Files.isReadable(privKey)) {
|
|
||||||
byte[] seed = Base64.getDecoder().decode(FileUtils.readFileToString(privKey.toFile(), StandardCharsets.ISO_8859_1));
|
|
||||||
EdDSAPrivateKeySpec privKeySpec = new EdDSAPrivateKeySpec(seed, keySpecs);
|
|
||||||
EdDSAPublicKeySpec pubKeySpec = new EdDSAPublicKeySpec(privKeySpec.getA(), keySpecs);
|
|
||||||
keys.add(new KeyPair(new EdDSAPublicKey(pubKeySpec), new EdDSAPrivateKey(privKeySpec)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (NoSuchAlgorithmException | IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getCurrentIndex() {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public KeyPair getKeys(int index) {
|
|
||||||
return keys.get(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
public PrivateKey getPrivateKey(int index) {
|
|
||||||
return getKeys(index).getPrivate();
|
|
||||||
}
|
|
||||||
|
|
||||||
public EdDSAPublicKey getPublicKey(int index) {
|
|
||||||
return (EdDSAPublicKey) getKeys(index).getPublic();
|
|
||||||
}
|
|
||||||
|
|
||||||
public EdDSAParameterSpec getSpecs() {
|
|
||||||
return keySpecs;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getPublicKeyBase64(int index) {
|
|
||||||
return Base64.getEncoder().encodeToString(getPublicKey(index).getAbyte());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@@ -33,7 +33,6 @@ public class SingleLookupReply {
|
|||||||
private static Gson gson = new Gson();
|
private static Gson gson = new Gson();
|
||||||
|
|
||||||
private boolean isRecursive;
|
private boolean isRecursive;
|
||||||
private boolean isSigned;
|
|
||||||
private String body;
|
private String body;
|
||||||
private SingleLookupRequest request;
|
private SingleLookupRequest request;
|
||||||
private _MatrixID mxid;
|
private _MatrixID mxid;
|
||||||
@@ -53,7 +52,6 @@ public class SingleLookupReply {
|
|||||||
reply.notAfter = Instant.ofEpochMilli(json.getNot_after());
|
reply.notAfter = Instant.ofEpochMilli(json.getNot_after());
|
||||||
reply.notBefore = Instant.ofEpochMilli(json.getNot_before());
|
reply.notBefore = Instant.ofEpochMilli(json.getNot_before());
|
||||||
reply.timestamp = Instant.ofEpochMilli(json.getTs());
|
reply.timestamp = Instant.ofEpochMilli(json.getTs());
|
||||||
reply.isSigned = json.isSigned();
|
|
||||||
} catch (JsonSyntaxException e) {
|
} catch (JsonSyntaxException e) {
|
||||||
// stub - we only want to try, nothing more
|
// stub - we only want to try, nothing more
|
||||||
}
|
}
|
||||||
@@ -85,10 +83,6 @@ public class SingleLookupReply {
|
|||||||
return isRecursive;
|
return isRecursive;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isSigned() {
|
|
||||||
return isSigned;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getBody() {
|
public String getBody() {
|
||||||
return body;
|
return body;
|
||||||
}
|
}
|
||||||
|
@@ -21,7 +21,7 @@
|
|||||||
package io.kamax.mxisd.lookup;
|
package io.kamax.mxisd.lookup;
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import io.kamax.mxisd.ThreePid;
|
import io.kamax.matrix.ThreePid;
|
||||||
|
|
||||||
public class ThreePidMapping {
|
public class ThreePidMapping {
|
||||||
|
|
||||||
|
@@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
package io.kamax.mxisd.lookup;
|
package io.kamax.mxisd.lookup;
|
||||||
|
|
||||||
import io.kamax.mxisd.ThreePid;
|
import io.kamax.matrix.ThreePid;
|
||||||
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@ public class ThreePidValidation extends ThreePid {
|
|||||||
private Instant validation;
|
private Instant validation;
|
||||||
|
|
||||||
public ThreePidValidation(ThreePid tpid, Instant validation) {
|
public ThreePidValidation(ThreePid tpid, Instant validation) {
|
||||||
super(tpid);
|
super(tpid.getMedium(), tpid.getAddress());
|
||||||
this.validation = validation;
|
this.validation = validation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -22,9 +22,6 @@ import java.util.Optional;
|
|||||||
// FIXME placeholder, this must go in matrix-java-sdk for 1.0
|
// FIXME placeholder, this must go in matrix-java-sdk for 1.0
|
||||||
public class IdentityServerUtils {
|
public class IdentityServerUtils {
|
||||||
|
|
||||||
public static final String THREEPID_TEST_MEDIUM = "email";
|
|
||||||
public static final String THREEPID_TEST_ADDRESS = "mxisd-email-forever-unknown@forever-invalid.kamax.io";
|
|
||||||
|
|
||||||
private static Logger log = LoggerFactory.getLogger(IdentityServerUtils.class);
|
private static Logger log = LoggerFactory.getLogger(IdentityServerUtils.class);
|
||||||
private static JsonParser parser = new JsonParser();
|
private static JsonParser parser = new JsonParser();
|
||||||
|
|
||||||
@@ -35,9 +32,7 @@ public class IdentityServerUtils {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// FIXME use Apache HTTP client
|
// FIXME use Apache HTTP client
|
||||||
HttpURLConnection rootSrvConn = (HttpURLConnection) new URL(
|
HttpURLConnection rootSrvConn = (HttpURLConnection) new URL(remote + "/_matrix/identity/api/v1/").openConnection();
|
||||||
remote + "/_matrix/identity/api/v1/lookup?medium=" + THREEPID_TEST_MEDIUM + "&address=" + THREEPID_TEST_ADDRESS
|
|
||||||
).openConnection();
|
|
||||||
// TODO turn this into a configuration property
|
// TODO turn this into a configuration property
|
||||||
rootSrvConn.setConnectTimeout(2000);
|
rootSrvConn.setConnectTimeout(2000);
|
||||||
|
|
||||||
@@ -53,11 +48,6 @@ public class IdentityServerUtils {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (el.getAsJsonObject().has("address")) {
|
|
||||||
log.debug("IS {} did not send back a JSON object for single 3PID lookup");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (IllegalArgumentException | IOException | JsonParseException e) {
|
} catch (IllegalArgumentException | IOException | JsonParseException e) {
|
||||||
log.info("{} is not a usable Identity Server: {}", remote, e.getMessage());
|
log.info("{} is not a usable Identity Server: {}", remote, e.getMessage());
|
||||||
@@ -84,39 +74,35 @@ public class IdentityServerUtils {
|
|||||||
|
|
||||||
List<SRVRecord> srvRecords = new ArrayList<>();
|
List<SRVRecord> srvRecords = new ArrayList<>();
|
||||||
Record[] records = new Lookup(lookupDns, Type.SRV).run();
|
Record[] records = new Lookup(lookupDns, Type.SRV).run();
|
||||||
if (records != null) {
|
if (records == null || records.length == 0) {
|
||||||
for (Record record : records) {
|
log.info("No SRV record for {}", lookupDns);
|
||||||
log.info("Record: {}", record.toString());
|
return Optional.empty();
|
||||||
if (record.getType() == Type.SRV) {
|
}
|
||||||
if (record instanceof SRVRecord) {
|
|
||||||
srvRecords.add((SRVRecord) record);
|
|
||||||
} else {
|
|
||||||
log.warn("We requested SRV records but we got {} instead!", record.getClass().getName());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log.warn("We request SRV type records but we got type #{} instead!", record.getType());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
srvRecords.sort(Comparator.comparingInt(SRVRecord::getPriority));
|
|
||||||
|
|
||||||
for (SRVRecord srvRecord : srvRecords) {
|
for (Record record : records) {
|
||||||
String baseUrl = "https://" + srvRecord.getTarget().toString(true) + ":" + srvRecord.getPort();
|
log.info("Record: {}", record.toString());
|
||||||
|
if (record.getType() == Type.SRV) {
|
||||||
|
if (record instanceof SRVRecord) {
|
||||||
|
srvRecords.add((SRVRecord) record);
|
||||||
|
} else {
|
||||||
|
log.warn("We requested SRV records but we got {} instead!", record.getClass().getName());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.warn("We request SRV type records but we got type #{} instead!", record.getType());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
srvRecords.sort(Comparator.comparingInt(SRVRecord::getPriority));
|
||||||
|
|
||||||
|
for (SRVRecord srvRecord : srvRecords) {
|
||||||
|
String baseUrl = "https://" + srvRecord.getTarget().toString(true) + ":" + srvRecord.getPort();
|
||||||
|
if (isUsable(baseUrl)) {
|
||||||
log.info("Found Identity Server for domain {} at {}", domainOrUrl, baseUrl);
|
log.info("Found Identity Server for domain {} at {}", domainOrUrl, baseUrl);
|
||||||
return Optional.of(baseUrl);
|
return Optional.of(baseUrl);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
log.info("No SRV record for {}", lookupDns);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info("Performing basic lookup using domain name {}", domainOrUrl);
|
log.info("Found no Identity server for domain {} at {}");
|
||||||
String baseUrl = "https://" + domainOrUrl;
|
return Optional.empty();
|
||||||
if (isUsable(baseUrl)) {
|
|
||||||
log.info("Found Identity Server for domain {} at {}", domainOrUrl, baseUrl);
|
|
||||||
return Optional.of(baseUrl);
|
|
||||||
} else {
|
|
||||||
log.info("{} is not a usable Identity Server", baseUrl);
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
|
||||||
} catch (TextParseException e) {
|
} catch (TextParseException e) {
|
||||||
log.warn(domainOrUrl + " is not a valid domain name");
|
log.warn(domainOrUrl + " is not a valid domain name");
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
|
58
src/main/java/io/kamax/mxisd/profile/ProfileManager.java
Normal file
58
src/main/java/io/kamax/mxisd/profile/ProfileManager.java
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* 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.profile;
|
||||||
|
|
||||||
|
import io.kamax.matrix._MatrixID;
|
||||||
|
import io.kamax.matrix._ThreePid;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class ProfileManager {
|
||||||
|
|
||||||
|
private List<ProfileProvider> providers;
|
||||||
|
|
||||||
|
public ProfileManager(List<ProfileProvider> providers) {
|
||||||
|
this.providers = providers.stream()
|
||||||
|
.filter(ProfileProvider::isEnabled)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> List<T> get(Function<ProfileProvider, List<T>> function) {
|
||||||
|
return providers.stream()
|
||||||
|
.map(function)
|
||||||
|
.flatMap(Collection::stream)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<_ThreePid> getThreepids(_MatrixID mxid) {
|
||||||
|
return get(p -> p.getThreepids(mxid));
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getRoles(_MatrixID mxid) {
|
||||||
|
return get(p -> p.getRoles(mxid));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
36
src/main/java/io/kamax/mxisd/profile/ProfileProvider.java
Normal file
36
src/main/java/io/kamax/mxisd/profile/ProfileProvider.java
Normal 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.profile;
|
||||||
|
|
||||||
|
import io.kamax.matrix._MatrixID;
|
||||||
|
import io.kamax.matrix._ThreePid;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface ProfileProvider {
|
||||||
|
|
||||||
|
boolean isEnabled();
|
||||||
|
|
||||||
|
List<_ThreePid> getThreepids(_MatrixID mxid);
|
||||||
|
|
||||||
|
List<String> getRoles(_MatrixID mxid);
|
||||||
|
|
||||||
|
}
|
@@ -25,9 +25,9 @@ import com.google.i18n.phonenumbers.NumberParseException;
|
|||||||
import com.google.i18n.phonenumbers.PhoneNumberUtil;
|
import com.google.i18n.phonenumbers.PhoneNumberUtil;
|
||||||
import com.google.i18n.phonenumbers.Phonenumber;
|
import com.google.i18n.phonenumbers.Phonenumber;
|
||||||
import io.kamax.matrix.MatrixID;
|
import io.kamax.matrix.MatrixID;
|
||||||
|
import io.kamax.matrix.ThreePid;
|
||||||
import io.kamax.matrix.ThreePidMedium;
|
import io.kamax.matrix.ThreePidMedium;
|
||||||
import io.kamax.matrix._MatrixID;
|
import io.kamax.matrix._MatrixID;
|
||||||
import io.kamax.mxisd.ThreePid;
|
|
||||||
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.controller.identity.v1.io.RequestTokenResponse;
|
import io.kamax.mxisd.controller.identity.v1.io.RequestTokenResponse;
|
||||||
|
@@ -1,79 +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.signature;
|
|
||||||
|
|
||||||
import com.google.gson.JsonObject;
|
|
||||||
import io.kamax.mxisd.config.ServerConfig;
|
|
||||||
import io.kamax.mxisd.exception.InternalServerError;
|
|
||||||
import io.kamax.mxisd.key.KeyManager;
|
|
||||||
import net.i2p.crypto.eddsa.EdDSAEngine;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
|
||||||
import java.security.InvalidKeyException;
|
|
||||||
import java.security.MessageDigest;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.security.SignatureException;
|
|
||||||
import java.util.Base64;
|
|
||||||
|
|
||||||
@Component
|
|
||||||
public class SignatureManager {
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private KeyManager keyMgr;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private ServerConfig srvCfg;
|
|
||||||
|
|
||||||
private EdDSAEngine signEngine;
|
|
||||||
|
|
||||||
private String sign(String message) {
|
|
||||||
try {
|
|
||||||
byte[] signRaw = signEngine.signOneShot(message.getBytes());
|
|
||||||
return Base64.getEncoder().encodeToString(signRaw);
|
|
||||||
} catch (SignatureException e) {
|
|
||||||
throw new InternalServerError(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public JsonObject signMessageGson(String message) {
|
|
||||||
String sign = sign(message);
|
|
||||||
|
|
||||||
JsonObject keySignature = new JsonObject();
|
|
||||||
keySignature.addProperty("ed25519:" + keyMgr.getCurrentIndex(), sign);
|
|
||||||
JsonObject signature = new JsonObject();
|
|
||||||
signature.add(srvCfg.getName(), keySignature);
|
|
||||||
|
|
||||||
return signature;
|
|
||||||
}
|
|
||||||
|
|
||||||
@PostConstruct
|
|
||||||
public void build() {
|
|
||||||
try {
|
|
||||||
signEngine = new EdDSAEngine(MessageDigest.getInstance(keyMgr.getSpecs().getHashAlgorithm()));
|
|
||||||
signEngine.initSign(keyMgr.getPrivateKey(keyMgr.getCurrentIndex()));
|
|
||||||
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
57
src/main/java/io/kamax/mxisd/spring/CryptoFactory.java
Normal file
57
src/main/java/io/kamax/mxisd/spring/CryptoFactory.java
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* mxisd - Matrix Identity Server Daemon
|
||||||
|
* Copyright (C) 2018 Kamax Sarl
|
||||||
|
*
|
||||||
|
* https://www.kamax.io/
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.kamax.mxisd.spring;
|
||||||
|
|
||||||
|
import io.kamax.matrix.crypto.KeyFileStore;
|
||||||
|
import io.kamax.matrix.crypto.KeyManager;
|
||||||
|
import io.kamax.matrix.crypto.SignatureManager;
|
||||||
|
import io.kamax.mxisd.config.KeyConfig;
|
||||||
|
import io.kamax.mxisd.config.MatrixConfig;
|
||||||
|
import org.apache.commons.io.FileUtils;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class CryptoFactory {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public KeyManager getKeyManager(KeyConfig keyCfg) {
|
||||||
|
File keyStore = new File(keyCfg.getPath());
|
||||||
|
if (!keyStore.exists()) {
|
||||||
|
try {
|
||||||
|
FileUtils.touch(keyStore);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new KeyManager(new KeyFileStore(keyCfg.getPath()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public SignatureManager getSignatureManager(KeyManager keyMgr, MatrixConfig mxCfg) {
|
||||||
|
return new SignatureManager(keyMgr, mxCfg.getDomain());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
package io.kamax.mxisd.storage;
|
package io.kamax.mxisd.storage;
|
||||||
|
|
||||||
import io.kamax.mxisd.ThreePid;
|
import io.kamax.matrix.ThreePid;
|
||||||
import io.kamax.mxisd.invitation.IThreePidInviteReply;
|
import io.kamax.mxisd.invitation.IThreePidInviteReply;
|
||||||
import io.kamax.mxisd.storage.dao.IThreePidSessionDao;
|
import io.kamax.mxisd.storage.dao.IThreePidSessionDao;
|
||||||
import io.kamax.mxisd.storage.ormlite.ThreePidInviteIO;
|
import io.kamax.mxisd.storage.ormlite.ThreePidInviteIO;
|
||||||
|
@@ -26,7 +26,7 @@ import com.j256.ormlite.dao.DaoManager;
|
|||||||
import com.j256.ormlite.jdbc.JdbcConnectionSource;
|
import com.j256.ormlite.jdbc.JdbcConnectionSource;
|
||||||
import com.j256.ormlite.support.ConnectionSource;
|
import com.j256.ormlite.support.ConnectionSource;
|
||||||
import com.j256.ormlite.table.TableUtils;
|
import com.j256.ormlite.table.TableUtils;
|
||||||
import io.kamax.mxisd.ThreePid;
|
import io.kamax.matrix.ThreePid;
|
||||||
import io.kamax.mxisd.exception.InternalServerError;
|
import io.kamax.mxisd.exception.InternalServerError;
|
||||||
import io.kamax.mxisd.invitation.IThreePidInviteReply;
|
import io.kamax.mxisd.invitation.IThreePidInviteReply;
|
||||||
import io.kamax.mxisd.storage.IStorage;
|
import io.kamax.mxisd.storage.IStorage;
|
||||||
|
@@ -22,7 +22,7 @@ package io.kamax.mxisd.storage.ormlite.dao;
|
|||||||
|
|
||||||
import com.j256.ormlite.field.DatabaseField;
|
import com.j256.ormlite.field.DatabaseField;
|
||||||
import com.j256.ormlite.table.DatabaseTable;
|
import com.j256.ormlite.table.DatabaseTable;
|
||||||
import io.kamax.mxisd.ThreePid;
|
import io.kamax.matrix.ThreePid;
|
||||||
import io.kamax.mxisd.storage.dao.IThreePidSessionDao;
|
import io.kamax.mxisd.storage.dao.IThreePidSessionDao;
|
||||||
|
|
||||||
@DatabaseTable(tableName = "session_3pid")
|
@DatabaseTable(tableName = "session_3pid")
|
||||||
|
@@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
package io.kamax.mxisd.threepid.notification;
|
package io.kamax.mxisd.threepid.notification;
|
||||||
|
|
||||||
import io.kamax.mxisd.ThreePid;
|
import io.kamax.matrix.ThreePid;
|
||||||
import io.kamax.mxisd.config.MatrixConfig;
|
import io.kamax.mxisd.config.MatrixConfig;
|
||||||
import io.kamax.mxisd.config.ServerConfig;
|
import io.kamax.mxisd.config.ServerConfig;
|
||||||
import io.kamax.mxisd.controller.identity.v1.IdentityAPIv1;
|
import io.kamax.mxisd.controller.identity.v1.IdentityAPIv1;
|
||||||
|
@@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
package io.kamax.mxisd.threepid.notification.email;
|
package io.kamax.mxisd.threepid.notification.email;
|
||||||
|
|
||||||
import io.kamax.mxisd.ThreePid;
|
import io.kamax.matrix.ThreePid;
|
||||||
import io.kamax.mxisd.config.MatrixConfig;
|
import io.kamax.mxisd.config.MatrixConfig;
|
||||||
import io.kamax.mxisd.config.ServerConfig;
|
import io.kamax.mxisd.config.ServerConfig;
|
||||||
import io.kamax.mxisd.config.threepid.medium.EmailConfig;
|
import io.kamax.mxisd.config.threepid.medium.EmailConfig;
|
||||||
|
@@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
package io.kamax.mxisd.threepid.session;
|
package io.kamax.mxisd.threepid.session;
|
||||||
|
|
||||||
import io.kamax.mxisd.ThreePid;
|
import io.kamax.matrix.ThreePid;
|
||||||
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
@@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
package io.kamax.mxisd.threepid.session;
|
package io.kamax.mxisd.threepid.session;
|
||||||
|
|
||||||
import io.kamax.mxisd.ThreePid;
|
import io.kamax.matrix.ThreePid;
|
||||||
import io.kamax.mxisd.exception.BadRequestException;
|
import io.kamax.mxisd.exception.BadRequestException;
|
||||||
import io.kamax.mxisd.exception.InvalidCredentialsException;
|
import io.kamax.mxisd.exception.InvalidCredentialsException;
|
||||||
import io.kamax.mxisd.storage.dao.IThreePidSessionDao;
|
import io.kamax.mxisd.storage.dao.IThreePidSessionDao;
|
||||||
@@ -76,7 +76,7 @@ public class ThreePidSession implements IThreePidSession {
|
|||||||
public ThreePidSession(String id, String server, ThreePid tPid, String secret, int attempt, String nextLink, String token) {
|
public ThreePidSession(String id, String server, ThreePid tPid, String secret, int attempt, String nextLink, String token) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.server = server;
|
this.server = server;
|
||||||
this.tPid = new ThreePid(tPid);
|
this.tPid = new ThreePid(tPid.getMedium(), tPid.getAddress());
|
||||||
this.secret = secret;
|
this.secret = secret;
|
||||||
this.attempt = attempt;
|
this.attempt = attempt;
|
||||||
this.nextLink = nextLink;
|
this.nextLink = nextLink;
|
||||||
|
33
src/main/java/io/kamax/mxisd/util/OptionalUtil.java
Normal file
33
src/main/java/io/kamax/mxisd/util/OptionalUtil.java
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* mxisd - Matrix Identity Server Daemon
|
||||||
|
* Copyright (C) 2018 Kamax Sarl
|
||||||
|
*
|
||||||
|
* https://www.kamax.io/
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.kamax.mxisd.util;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
public class OptionalUtil {
|
||||||
|
|
||||||
|
public static <T> Optional<T> findFirst(Supplier<Optional<T>>... suppliers) {
|
||||||
|
return Stream.of(suppliers).map(Supplier::get).filter(Optional::isPresent).map(Optional::get).findFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -20,9 +20,7 @@
|
|||||||
|
|
||||||
package io.kamax.mxisd.util;
|
package io.kamax.mxisd.util;
|
||||||
|
|
||||||
import com.google.gson.FieldNamingPolicy;
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.GsonBuilder;
|
|
||||||
import org.apache.http.client.methods.HttpPost;
|
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;
|
||||||
@@ -31,7 +29,7 @@ import java.nio.charset.StandardCharsets;
|
|||||||
|
|
||||||
public class RestClientUtils {
|
public class RestClientUtils {
|
||||||
|
|
||||||
private static Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
|
private static Gson gson = GsonUtil.build();
|
||||||
|
|
||||||
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);
|
||||||
|
@@ -151,10 +151,27 @@ sql:
|
|||||||
identity:
|
identity:
|
||||||
type: 'mxid'
|
type: 'mxid'
|
||||||
query: 'SELECT user_id AS uid FROM user_threepids WHERE medium = ? AND address = ?'
|
query: 'SELECT user_id AS uid FROM user_threepids WHERE medium = ? AND address = ?'
|
||||||
|
profile:
|
||||||
|
threepid:
|
||||||
|
query: 'SELECT medium, address FROM user_threepids WHERE user_id = ?'
|
||||||
|
|
||||||
synapseSql:
|
synapseSql:
|
||||||
enabled: false
|
enabled: false
|
||||||
type: 'sqlite'
|
type: 'sqlite'
|
||||||
|
profile:
|
||||||
|
threepid:
|
||||||
|
query: 'SELECT medium, address FROM user_threepids WHERE user_id = ?'
|
||||||
|
|
||||||
|
wordpress:
|
||||||
|
enabled: false
|
||||||
|
sql:
|
||||||
|
type: 'mysql'
|
||||||
|
query:
|
||||||
|
threepid:
|
||||||
|
email: 'SELECT user_login as uid FROM wp_users WHERE user_email = ?'
|
||||||
|
directory:
|
||||||
|
name: "SELECT DISTINCT user_login, display_name FROM wp_users u LEFT JOIN wp_usermeta m ON m.user_id = u.id WHERE u.display_name LIKE ? OR (m.meta_key = 'nickname' AND m.meta_value = ?) OR (m.meta_key = 'first_name' AND m.meta_value = ?) OR (m.meta_key = 'last_name' AND m.meta_value = ?);"
|
||||||
|
threepid: 'SELECT DISTINCT user_login, display_name FROM wp_users WHERE user_email LIKE ?'
|
||||||
|
|
||||||
forward:
|
forward:
|
||||||
servers:
|
servers:
|
||||||
|
Reference in New Issue
Block a user