diff --git a/build.gradle b/build.gradle
index 053837f..362d260 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,5 +1,3 @@
-import java.util.regex.Pattern
-
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2017 Maxime Dor
@@ -20,7 +18,9 @@ import java.util.regex.Pattern
* along with this program. If not, see .
*/
-apply plugin: 'groovy'
+import java.util.regex.Pattern
+
+apply plugin: 'java'
apply plugin: 'org.springframework.boot'
def confFileName = "application.example.yaml"
@@ -70,9 +70,6 @@ repositories {
}
dependencies {
- // We are a groovy project
- compile 'org.codehaus.groovy:groovy-all:2.4.7'
-
// Easy file management
compile 'commons-io:commons-io:2.5'
diff --git a/src/main/groovy/io/kamax/mxisd/backend/firebase/GoogleFirebaseAuthenticator.groovy b/src/main/groovy/io/kamax/mxisd/backend/firebase/GoogleFirebaseAuthenticator.groovy
deleted file mode 100644
index 4533f3a..0000000
--- a/src/main/groovy/io/kamax/mxisd/backend/firebase/GoogleFirebaseAuthenticator.groovy
+++ /dev/null
@@ -1,193 +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 .
- */
-
-package io.kamax.mxisd.backend.firebase
-
-import com.google.firebase.FirebaseApp
-import com.google.firebase.FirebaseOptions
-import com.google.firebase.auth.*
-import com.google.firebase.internal.NonNull
-import com.google.firebase.tasks.OnFailureListener
-import com.google.firebase.tasks.OnSuccessListener
-import io.kamax.matrix.ThreePidMedium
-import io.kamax.matrix._MatrixID
-import io.kamax.mxisd.ThreePid
-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 java.util.concurrent.CountDownLatch
-import java.util.concurrent.TimeUnit
-import java.util.regex.Pattern
-
-public class GoogleFirebaseAuthenticator implements AuthenticatorProvider {
-
- private Logger log = LoggerFactory.getLogger(GoogleFirebaseAuthenticator.class);
-
- private static final Pattern matrixIdLaxPattern = Pattern.compile("@(.*):(.+)"); // FIXME use matrix-java-sdk
-
- private boolean isEnabled;
- private String domain;
- private FirebaseApp fbApp;
- private FirebaseAuth fbAuth;
-
- private void waitOnLatch(BackendAuthResult result, CountDownLatch l, long timeout, TimeUnit unit, String purpose) {
- try {
- l.await(timeout, unit);
- } catch (InterruptedException e) {
- log.warn("Interrupted while waiting for " + purpose);
- result.failure();
- }
- }
-
- public GoogleFirebaseAuthenticator(boolean isEnabled) {
- this.isEnabled = isEnabled;
- }
-
- public GoogleFirebaseAuthenticator(String credsPath, String db, String domain) {
- this(true);
- this.domain = domain;
- try {
- fbApp = FirebaseApp.initializeApp(getOpts(credsPath, db), "AuthenticationProvider");
- fbAuth = FirebaseAuth.getInstance(fbApp);
-
- log.info("Google Firebase Authentication is ready");
- } catch (IOException e) {
- throw new RuntimeException("Error when initializing Firebase", e);
- }
- }
-
- private FirebaseCredential getCreds(String credsPath) throws IOException {
- if (StringUtils.isNotBlank(credsPath)) {
- return FirebaseCredentials.fromCertificate(new FileInputStream(credsPath));
- } else {
- return FirebaseCredentials.applicationDefault();
- }
- }
-
- private FirebaseOptions getOpts(String credsPath, String db) throws IOException {
- if (StringUtils.isBlank(db)) {
- throw new IllegalArgumentException("Firebase database is not configured");
- }
-
- return new FirebaseOptions.Builder()
- .setCredential(getCreds(credsPath))
- .setDatabaseUrl(db)
- .build();
- }
-
- @Override
- public boolean isEnabled() {
- return isEnabled;
- }
-
- private void waitOnLatch(CountDownLatch l) {
- try {
- l.await(30, TimeUnit.SECONDS);
- } catch (InterruptedException e) {
- log.warn("Interrupted while waiting for Firebase auth check");
- }
- }
-
- @Override
- public BackendAuthResult authenticate(_MatrixID mxid, String password) {
- if (!isEnabled()) {
- throw new IllegalStateException();
- }
-
- log.info("Trying to authenticate {}", mxid);
-
- BackendAuthResult result = BackendAuthResult.failure();
-
- String localpart = m.group(1);
- CountDownLatch l = new CountDownLatch(1);
- fbAuth.verifyIdToken(password).addOnSuccessListener(new OnSuccessListener() {
- @Override
- void onSuccess(FirebaseToken token) {
- try {
- if (!StringUtils.equals(localpart, token.getUid())) {
- log.info("Failture to authenticate {}: Matrix ID localpart '{}' does not match Firebase UID '{}'", id, localpart, token.getUid());
- result = BackendAuthResult.failure();
- return;
- }
-
- result = BackendAuthResult.success(mxid.getId(), UserIdType.MatrixID, token.getName());
- log.info("{} was successfully authenticated", mxid);
- log.info("Fetching profile for {}", mxid);
- CountDownLatch userRecordLatch = new CountDownLatch(1);
- fbAuth.getUser(token.getUid()).addOnSuccessListener(new OnSuccessListener() {
- @Override
- void onSuccess(UserRecord user) {
- try {
- if (StringUtils.isNotBlank(user.getEmail())) {
- result.withThreePid(new ThreePid(ThreePidMedium.Email.getId(), user.getEmail()));
- }
-
- if (StringUtils.isNotBlank(user.getPhoneNumber())) {
- result.withThreePid(new ThreePid(ThreePidMedium.PhoneNumber.getId(), user.getPhoneNumber()));
- }
-
- } finally {
- userRecordLatch.countDown();
- }
- }
- }).addOnFailureListener(new OnFailureListener() {
- @Override
- void onFailure(@NonNull Exception e) {
- try {
- log.warn("Unable to fetch Firebase user profile for {}", mxid);
- result = BackendAuthResult.failure();
- } finally {
- userRecordLatch.countDown();
- }
- }
- });
-
- waitOnLatch(result, userRecordLatch, 30, TimeUnit.SECONDS, "Firebase user profile");
- } finally {
- l.countDown()
- }
- }
- }).addOnFailureListener(new OnFailureListener() {
- @Override
- void onFailure(@NonNull Exception e) {
- try {
- if (e instanceof IllegalArgumentException) {
- log.info("Failure to authenticate {}: invalid firebase token", mxid);
- } else {
- log.info("Failure to authenticate {}: {}", id, e.getMessage(), e);
- log.info("Exception", e);
- }
-
- result = BackendAuthResult.failure();
- } finally {
- l.countDown()
- }
- }
- });
-
- waitOnLatch(result, l, 30, TimeUnit.SECONDS, "Firebase auth check");
- return result;
- }
-
-}
diff --git a/src/main/groovy/io/kamax/mxisd/backend/ldap/LdapThreePidProvider.groovy b/src/main/groovy/io/kamax/mxisd/backend/ldap/LdapThreePidProvider.groovy
deleted file mode 100644
index d30c7f9..0000000
--- a/src/main/groovy/io/kamax/mxisd/backend/ldap/LdapThreePidProvider.groovy
+++ /dev/null
@@ -1,169 +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 .
- */
-
-package io.kamax.mxisd.backend.ldap
-
-import io.kamax.mxisd.config.MatrixConfig
-import io.kamax.mxisd.lookup.SingleLookupReply
-import io.kamax.mxisd.lookup.SingleLookupRequest
-import io.kamax.mxisd.lookup.ThreePidMapping
-import io.kamax.mxisd.lookup.provider.IThreePidProvider
-import org.apache.commons.lang.StringUtils
-import org.apache.directory.api.ldap.model.cursor.CursorLdapReferralException
-import org.apache.directory.api.ldap.model.cursor.EntryCursor
-import org.apache.directory.api.ldap.model.entry.Attribute
-import org.apache.directory.api.ldap.model.entry.Entry
-import org.apache.directory.api.ldap.model.message.SearchScope
-import org.apache.directory.ldap.client.api.LdapConnection
-import org.slf4j.Logger
-import org.slf4j.LoggerFactory
-import org.springframework.beans.factory.annotation.Autowired
-import org.springframework.stereotype.Component
-
-@Component
-class LdapThreePidProvider extends LdapGenericBackend implements IThreePidProvider {
-
- public static final String UID = "uid"
- public static final String MATRIX_ID = "mxid"
-
- private Logger log = LoggerFactory.getLogger(LdapThreePidProvider.class)
-
- @Autowired
- private MatrixConfig mxCfg
-
- @Override
- boolean isEnabled() {
- return getCfg().isEnabled()
- }
-
- private String getUidAttribute() {
- return getCfg().getAttribute().getUid().getValue();
- }
-
- @Override
- boolean isLocal() {
- return true
- }
-
- @Override
- int getPriority() {
- return 20
- }
-
- Optional lookup(LdapConnection conn, String medium, String value) {
- String uidAttribute = getUidAttribute()
-
- Optional queryOpt = getCfg().getIdentity().getQuery(medium)
- if (!queryOpt.isPresent()) {
- log.warn("{} is not a configured 3PID type for LDAP lookup", medium)
- return Optional.empty()
- }
-
- String searchQuery = queryOpt.get().replaceAll("%3pid", value)
- EntryCursor cursor = conn.search(getCfg().getConn().getBaseDn(), searchQuery, SearchScope.SUBTREE, uidAttribute)
- try {
- while (cursor.next()) {
- Entry entry = cursor.get()
- log.info("Found possible match, DN: {}", entry.getDn().getName())
-
- Attribute attribute = entry.get(uidAttribute)
- if (attribute == null) {
- log.info("DN {}: no attribute {}, skpping", entry.getDn(), getCfg().getAttribute())
- continue
- }
-
- String data = attribute.get().toString()
- if (data.length() < 1) {
- log.info("DN {}: empty attribute {}, skipping", getCfg().getAttribute())
- continue
- }
-
- StringBuilder matrixId = new StringBuilder()
- // TODO Should we turn this block into a map of functions?
- String uidType = getCfg().getAttribute().getUid().getType()
- if (StringUtils.equals(UID, uidType)) {
- matrixId.append("@").append(data).append(":").append(mxCfg.getDomain())
- } else if (StringUtils.equals(MATRIX_ID, uidType)) {
- matrixId.append(data)
- } else {
- log.warn("Bind was found but type {} is not supported", uidType)
- continue
- }
-
- log.info("DN {} is a valid match", entry.getDn().getName())
- return Optional.of(matrixId.toString())
- }
- } catch (CursorLdapReferralException e) {
- log.warn("3PID {} is only available via referral, skipping", value)
- } finally {
- cursor.close()
- }
-
- return Optional.empty()
- }
-
- @Override
- Optional find(SingleLookupRequest request) {
- log.info("Performing LDAP lookup ${request.getThreePid()} of type ${request.getType()}")
-
- LdapConnection conn = getConn()
- try {
- bind(conn)
-
- Optional mxid = lookup(conn, request.getType(), request.getThreePid())
- if (mxid.isPresent()) {
- return Optional.of(new SingleLookupReply(request, mxid.get()));
- }
- } finally {
- conn.close()
- }
-
- log.info("No match found")
- return Optional.empty()
- }
-
- @Override
- List populate(List mappings) {
- log.info("Looking up {} mappings", mappings.size())
- List mappingsFound = new ArrayList<>()
-
- LdapConnection conn = getConn()
- try {
- bind(conn)
-
- for (ThreePidMapping mapping : mappings) {
- try {
- Optional mxid = lookup(conn, mapping.getMedium(), mapping.getValue())
- if (mxid.isPresent()) {
- mapping.setMxid(mxid.get())
- mappingsFound.add(mapping)
- }
- } catch (IllegalArgumentException e) {
- log.warn("{} is not a supported 3PID type for LDAP lookup", mapping.getMedium())
- }
- }
- } finally {
- conn.close()
- }
-
- return mappingsFound
- }
-
-}
diff --git a/src/main/groovy/io/kamax/mxisd/config/RecursiveLookupBridgeConfig.groovy b/src/main/groovy/io/kamax/mxisd/config/RecursiveLookupBridgeConfig.groovy
deleted file mode 100644
index 9830376..0000000
--- a/src/main/groovy/io/kamax/mxisd/config/RecursiveLookupBridgeConfig.groovy
+++ /dev/null
@@ -1,83 +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 .
- */
-
-package io.kamax.mxisd.config
-
-import org.slf4j.Logger
-import org.slf4j.LoggerFactory
-import org.springframework.beans.factory.InitializingBean
-import org.springframework.boot.context.properties.ConfigurationProperties
-import org.springframework.context.annotation.Configuration
-
-@Configuration
-@ConfigurationProperties(prefix = "lookup.recursive.bridge")
-class RecursiveLookupBridgeConfig implements InitializingBean {
-
- private Logger log = LoggerFactory.getLogger(RecursiveLookupBridgeConfig.class)
-
- private boolean enabled
- private boolean recursiveOnly
- private String server
- private Map mappings = new HashMap<>()
-
- boolean getEnabled() {
- return enabled
- }
-
- void setEnabled(boolean enabled) {
- this.enabled = enabled
- }
-
- boolean getRecursiveOnly() {
- return recursiveOnly
- }
-
- void setRecursiveOnly(boolean recursiveOnly) {
- this.recursiveOnly = recursiveOnly
- }
-
- String getServer() {
- return server
- }
-
- void setServer(String server) {
- this.server = server
- }
-
- Map getMappings() {
- return mappings
- }
-
- void setMappings(Map mappings) {
- this.mappings = mappings
- }
-
- @Override
- void afterPropertiesSet() throws Exception {
- log.info("--- Bridge integration lookups config ---")
- log.info("Enabled: {}", getEnabled())
- if (getEnabled()) {
- log.info("Recursive only: {}", getRecursiveOnly())
- log.info("Fallback Server: {}", getServer())
- log.info("Mappings: {}", mappings.size())
- }
- }
-
-}
diff --git a/src/main/groovy/io/kamax/mxisd/config/ldap/LdapConfig.groovy b/src/main/groovy/io/kamax/mxisd/config/ldap/LdapConfig.groovy
deleted file mode 100644
index 0f9428d..0000000
--- a/src/main/groovy/io/kamax/mxisd/config/ldap/LdapConfig.groovy
+++ /dev/null
@@ -1,129 +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 .
- */
-
-package io.kamax.mxisd.config.ldap
-
-import groovy.json.JsonOutput
-import io.kamax.mxisd.backend.ldap.LdapThreePidProvider
-import org.apache.commons.lang.StringUtils
-import org.slf4j.Logger
-import org.slf4j.LoggerFactory
-import org.springframework.beans.factory.annotation.Autowired
-import org.springframework.boot.context.properties.ConfigurationProperties
-import org.springframework.context.annotation.Configuration
-
-import javax.annotation.PostConstruct
-
-@Configuration
-@ConfigurationProperties(prefix = "ldap")
-class LdapConfig {
-
- private Logger log = LoggerFactory.getLogger(LdapConfig.class)
-
- private boolean enabled
-
- @Autowired
- private LdapConnectionConfig conn
- private LdapAttributeConfig attribute
- private LdapAuthConfig auth
- private LdapIdentityConfig identity
-
- boolean isEnabled() {
- return enabled
- }
-
- void setEnabled(boolean enabled) {
- this.enabled = enabled
- }
-
- LdapConnectionConfig getConn() {
- return conn
- }
-
- void setConn(LdapConnectionConfig conn) {
- this.conn = conn
- }
-
- LdapAttributeConfig getAttribute() {
- return attribute
- }
-
- void setAttribute(LdapAttributeConfig attribute) {
- this.attribute = attribute
- }
-
- LdapAuthConfig getAuth() {
- return auth
- }
-
- void setAuth(LdapAuthConfig auth) {
- this.auth = auth
- }
-
- LdapIdentityConfig getIdentity() {
- return identity
- }
-
- void setIdentity(LdapIdentityConfig identity) {
- this.identity = identity
- }
-
- @PostConstruct
- void afterPropertiesSet() {
- log.info("--- LDAP Config ---")
- log.info("Enabled: {}", isEnabled())
-
- if (!isEnabled()) {
- return
- }
-
- if (StringUtils.isBlank(conn.getHost())) {
- throw new IllegalStateException("LDAP Host must be configured!")
- }
-
- if (1 > conn.getPort() || 65535 < conn.getPort()) {
- throw new IllegalStateException("LDAP port is not valid")
- }
-
- if (StringUtils.isBlank(attribute.getUid().getType())) {
- throw new IllegalStateException("Attribute UID Type cannot be empty")
- }
-
-
- if (StringUtils.isBlank(attribute.getUid().getValue())) {
- throw new IllegalStateException("Attribute UID value cannot be empty")
- }
-
- String uidType = attribute.getUid().getType();
- if (!StringUtils.equals(LdapThreePidProvider.UID, uidType) && !StringUtils.equals(LdapThreePidProvider.MATRIX_ID, uidType)) {
- throw new IllegalArgumentException("Unsupported LDAP UID type: " + uidType)
- }
-
- log.info("Host: {}", conn.getHost())
- log.info("Port: {}", conn.getPort())
- log.info("Bind DN: {}", conn.getBindDn())
- log.info("Base DN: {}", conn.getBaseDn())
-
- log.info("Attribute: {}", JsonOutput.toJson(attribute))
- log.info("Auth: {}", JsonOutput.toJson(auth))
- log.info("Identity: {}", JsonOutput.toJson(identity))
- }
-
-}
diff --git a/src/main/groovy/io/kamax/mxisd/controller/v1/MappingController.groovy b/src/main/groovy/io/kamax/mxisd/controller/v1/MappingController.groovy
deleted file mode 100644
index 945a282..0000000
--- a/src/main/groovy/io/kamax/mxisd/controller/v1/MappingController.groovy
+++ /dev/null
@@ -1,121 +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 .
- */
-
-package io.kamax.mxisd.controller.v1
-
-import com.google.gson.Gson
-import com.google.gson.JsonObject
-import groovy.json.JsonOutput
-import groovy.json.JsonSlurper
-import io.kamax.mxisd.controller.v1.io.SingeLookupReplyJson
-import io.kamax.mxisd.lookup.*
-import io.kamax.mxisd.lookup.strategy.LookupStrategy
-import io.kamax.mxisd.signature.SignatureManager
-import org.apache.commons.lang.StringUtils
-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.RequestMapping
-import org.springframework.web.bind.annotation.RequestParam
-import org.springframework.web.bind.annotation.RestController
-
-import javax.servlet.http.HttpServletRequest
-
-import static org.springframework.web.bind.annotation.RequestMethod.GET
-import static org.springframework.web.bind.annotation.RequestMethod.POST
-
-@RestController
-@CrossOrigin
-@RequestMapping(path = IdentityAPIv1.BASE, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
-class MappingController {
-
- private Logger log = LoggerFactory.getLogger(MappingController.class)
- private JsonSlurper json = new JsonSlurper()
- private Gson gson = new Gson()
-
- @Autowired
- private LookupStrategy strategy
-
- @Autowired
- private SignatureManager signMgr
-
- private void setRequesterInfo(ALookupRequest lookupReq, HttpServletRequest req) {
- lookupReq.setRequester(req.getRemoteAddr())
- String xff = req.getHeader("X-FORWARDED-FOR")
- lookupReq.setRecursive(StringUtils.isNotBlank(xff))
- if (lookupReq.isRecursive()) {
- lookupReq.setRecurseHosts(Arrays.asList(xff.split(",")))
- }
-
- lookupReq.setUserAgent(req.getHeader("USER-AGENT"))
- }
-
- @RequestMapping(value = "/lookup", method = GET)
- String lookup(HttpServletRequest request, @RequestParam String medium, @RequestParam String address) {
- SingleLookupRequest lookupRequest = new SingleLookupRequest()
- setRequesterInfo(lookupRequest, request)
- lookupRequest.setType(medium)
- lookupRequest.setThreePid(address)
-
- log.info("Got single lookup request from {} with client {} - Is recursive? {}", lookupRequest.getRequester(), lookupRequest.getUserAgent(), lookupRequest.isRecursive())
-
- Optional lookupOpt = strategy.find(lookupRequest)
- if (!lookupOpt.isPresent()) {
- log.info("No mapping was found, return empty JSON object")
- return "{}"
- }
-
- 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 = new Gson().toJsonTree(new SingeLookupReplyJson(lookup)).getAsJsonObject()
- obj.add("signatures", signMgr.signMessageGson(gson.toJson(obj)))
-
- return gson.toJson(obj)
- }
- }
-
- @RequestMapping(value = "/bulk_lookup", method = POST)
- String bulkLookup(HttpServletRequest request) {
- BulkLookupRequest lookupRequest = new BulkLookupRequest()
- setRequesterInfo(lookupRequest, request)
- log.info("Got single lookup request from {} with client {} - Is recursive? {}", lookupRequest.getRequester(), lookupRequest.getUserAgent(), lookupRequest.isRecursive())
-
- ClientBulkLookupRequest input = (ClientBulkLookupRequest) json.parseText(request.getInputStream().getText())
- List mappings = new ArrayList<>()
- for (List mappingRaw : input.getThreepids()) {
- ThreePidMapping mapping = new ThreePidMapping()
- mapping.setMedium(mappingRaw.get(0))
- mapping.setValue(mappingRaw.get(1))
- mappings.add(mapping)
- }
- lookupRequest.setMappings(mappings)
-
- ClientBulkLookupAnswer answer = new ClientBulkLookupAnswer()
- answer.addAll(strategy.find(lookupRequest))
- return JsonOutput.toJson(answer)
- }
-
-}
diff --git a/src/main/groovy/io/kamax/mxisd/key/KeyManager.groovy b/src/main/groovy/io/kamax/mxisd/key/KeyManager.groovy
deleted file mode 100644
index 6be9227..0000000
--- a/src/main/groovy/io/kamax/mxisd/key/KeyManager.groovy
+++ /dev/null
@@ -1,106 +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 .
- */
-
-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.InitializingBean
-import org.springframework.beans.factory.annotation.Autowired
-import org.springframework.stereotype.Component
-
-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.PrivateKey
-
-@Component
-class KeyManager implements InitializingBean {
-
- @Autowired
- private KeyConfig keyCfg
-
- private EdDSAParameterSpec keySpecs
- private EdDSAEngine signEngine
- private List keys
-
- @Override
- void afterPropertiesSet() throws Exception {
- 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)))
- }
- }
- }
-
- int getCurrentIndex() {
- return 0
- }
-
- KeyPair getKeys(int index) {
- return keys.get(index)
- }
-
- PrivateKey getPrivateKey(int index) {
- return getKeys(index).getPrivate()
- }
-
- EdDSAPublicKey getPublicKey(int index) {
- return (EdDSAPublicKey) getKeys(index).getPublic()
- }
-
- EdDSAParameterSpec getSpecs() {
- return keySpecs
- }
-
- String getPublicKeyBase64(int index) {
- return Base64.getEncoder().encodeToString(getPublicKey(index).getAbyte())
- }
-
-}
diff --git a/src/main/groovy/io/kamax/mxisd/lookup/provider/RemoteIdentityServerFetcher.groovy b/src/main/groovy/io/kamax/mxisd/lookup/provider/RemoteIdentityServerFetcher.groovy
deleted file mode 100644
index 5844d9c..0000000
--- a/src/main/groovy/io/kamax/mxisd/lookup/provider/RemoteIdentityServerFetcher.groovy
+++ /dev/null
@@ -1,135 +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 .
- */
-
-package io.kamax.mxisd.lookup.provider
-
-import groovy.json.JsonException
-import groovy.json.JsonOutput
-import groovy.json.JsonSlurper
-import io.kamax.mxisd.controller.v1.ClientBulkLookupRequest
-import io.kamax.mxisd.lookup.SingleLookupReply
-import io.kamax.mxisd.lookup.SingleLookupRequest
-import io.kamax.mxisd.lookup.ThreePidMapping
-import io.kamax.mxisd.lookup.fetcher.IRemoteIdentityServerFetcher
-import io.kamax.mxisd.matrix.IdentityServerUtils
-import org.apache.http.HttpEntity
-import org.apache.http.HttpResponse
-import org.apache.http.client.HttpClient
-import org.apache.http.client.entity.EntityBuilder
-import org.apache.http.client.methods.HttpPost
-import org.apache.http.entity.ContentType
-import org.apache.http.impl.client.HttpClients
-import org.slf4j.Logger
-import org.slf4j.LoggerFactory
-import org.springframework.context.annotation.Lazy
-import org.springframework.context.annotation.Scope
-import org.springframework.stereotype.Component
-
-@Component
-@Scope("prototype")
-@Lazy
-public class RemoteIdentityServerFetcher implements IRemoteIdentityServerFetcher {
-
- private Logger log = LoggerFactory.getLogger(RemoteIdentityServerFetcher.class)
-
- private JsonSlurper json = new JsonSlurper()
-
- @Override
- boolean isUsable(String remote) {
- return IdentityServerUtils.isUsable(remote)
- }
-
- @Override
- Optional find(String remote, SingleLookupRequest request) {
- log.info("Looking up {} 3PID {} using {}", request.getType(), request.getThreePid(), remote)
-
- HttpURLConnection rootSrvConn = (HttpURLConnection) new URL(
- "${remote}/_matrix/identity/api/v1/lookup?medium=${request.getType()}&address=${request.getThreePid()}"
- ).openConnection()
-
- try {
- String outputRaw = rootSrvConn.getInputStream().getText()
- def output = json.parseText(outputRaw)
- if (output['address']) {
- log.info("Found 3PID mapping: {}", output)
-
- return Optional.of(SingleLookupReply.fromRecursive(request, outputRaw))
- }
-
- log.info("Empty 3PID mapping from {}", remote)
- return Optional.empty()
- } catch (IOException e) {
- log.warn("Error looking up 3PID mapping {}: {}", request.getThreePid(), e.getMessage())
- return Optional.empty()
- } catch (JsonException e) {
- log.warn("Invalid JSON answer from {}", remote)
- return Optional.empty()
- }
- }
-
- @Override
- List find(String remote, List mappings) {
- List mappingsFound = new ArrayList<>()
-
- ClientBulkLookupRequest mappingRequest = new ClientBulkLookupRequest()
- mappingRequest.setMappings(mappings)
-
- String url = "${remote}/_matrix/identity/api/v1/bulk_lookup"
- HttpClient client = HttpClients.createDefault()
- try {
- HttpPost request = new HttpPost(url)
- request.setEntity(
- EntityBuilder.create()
- .setText(JsonOutput.toJson(mappingRequest))
- .setContentType(ContentType.APPLICATION_JSON)
- .build()
- )
-
- HttpResponse response = client.execute(request)
- try {
- if (response.getStatusLine().getStatusCode() != 200) {
- log.info("Could not perform lookup at {} due to HTTP return code: {}", url, response.getStatusLine().getStatusCode())
- return mappingsFound
- }
-
- HttpEntity entity = response.getEntity()
- if (entity != null) {
- ClientBulkLookupRequest input = (ClientBulkLookupRequest) json.parseText(entity.getContent().getText())
- for (List mappingRaw : input.getThreepids()) {
- ThreePidMapping mapping = new ThreePidMapping()
- mapping.setMedium(mappingRaw.get(0))
- mapping.setValue(mappingRaw.get(1))
- mapping.setMxid(mappingRaw.get(2))
- mappingsFound.add(mapping)
- }
- } else {
- log.info("HTTP response from {} was empty", remote)
- }
-
- return mappingsFound
- } finally {
- response.close()
- }
- } finally {
- client.close()
- }
- }
-
-}
diff --git a/src/main/groovy/io/kamax/mxisd/lookup/strategy/RecursivePriorityLookupStrategy.groovy b/src/main/groovy/io/kamax/mxisd/lookup/strategy/RecursivePriorityLookupStrategy.groovy
deleted file mode 100644
index 048dd21..0000000
--- a/src/main/groovy/io/kamax/mxisd/lookup/strategy/RecursivePriorityLookupStrategy.groovy
+++ /dev/null
@@ -1,210 +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 .
- */
-
-package io.kamax.mxisd.lookup.strategy
-
-import edazdarevic.commons.net.CIDRUtils
-import io.kamax.mxisd.config.RecursiveLookupConfig
-import io.kamax.mxisd.lookup.*
-import io.kamax.mxisd.lookup.fetcher.IBridgeFetcher
-import io.kamax.mxisd.lookup.provider.IThreePidProvider
-import org.slf4j.Logger
-import org.slf4j.LoggerFactory
-import org.springframework.beans.factory.InitializingBean
-import org.springframework.beans.factory.annotation.Autowired
-import org.springframework.stereotype.Component
-
-import java.util.function.Predicate
-import java.util.stream.Collectors
-
-@Component
-class RecursivePriorityLookupStrategy implements LookupStrategy, InitializingBean {
-
- private Logger log = LoggerFactory.getLogger(RecursivePriorityLookupStrategy.class)
-
- @Autowired
- private RecursiveLookupConfig recursiveCfg
-
- @Autowired
- private List providers
-
- @Autowired
- private IBridgeFetcher bridge
-
- private List allowedCidr = new ArrayList<>()
-
- @Override
- void afterPropertiesSet() throws Exception {
- log.info("Found ${providers.size()} providers")
-
- providers.sort(new Comparator() {
-
- @Override
- int compare(IThreePidProvider o1, IThreePidProvider o2) {
- return Integer.compare(o2.getPriority(), o1.getPriority())
- }
-
- })
-
- log.info("Recursive lookup enabled: {}", recursiveCfg.isEnabled())
- for (String cidr : recursiveCfg.getAllowedCidr()) {
- log.info("{} is allowed for recursion", cidr)
- allowedCidr.add(new CIDRUtils(cidr))
- }
- }
-
- boolean isAllowedForRecursive(String source) {
- boolean canRecurse = false
-
- if (recursiveCfg.isEnabled()) {
- log.debug("Checking {} CIDRs for recursion", allowedCidr.size())
- for (CIDRUtils cidr : allowedCidr) {
- if (cidr.isInRange(source)) {
- log.debug("{} is in range {}, allowing recursion", source, cidr.getNetworkAddress())
- canRecurse = true
- break
- } else {
- log.debug("{} is not in range {}", source, cidr.getNetworkAddress())
- }
- }
- }
-
- return canRecurse
- }
-
- List listUsableProviders(ALookupRequest request) {
- return listUsableProviders(request, false);
- }
-
- List listUsableProviders(ALookupRequest request, boolean forceRecursive) {
- List usableProviders = new ArrayList<>()
-
- boolean canRecurse = forceRecursive || isAllowedForRecursive(request.getRequester())
-
- log.info("Host {} allowed for recursion: {}", request.getRequester(), canRecurse)
- for (IThreePidProvider provider : providers) {
- if (provider.isEnabled() && (provider.isLocal() || canRecurse || forceRecursive)) {
- usableProviders.add(provider)
- }
- }
-
- return usableProviders
- }
-
- @Override
- List getLocalProviders() {
- return providers.stream().filter(new Predicate() {
- @Override
- boolean test(IThreePidProvider iThreePidProvider) {
- return iThreePidProvider.isEnabled() && iThreePidProvider.isLocal()
- }
- }).collect(Collectors.toList())
- }
-
- List getRemoteProviders() {
- return providers.stream().filter(new Predicate() {
- @Override
- boolean test(IThreePidProvider iThreePidProvider) {
- return iThreePidProvider.isEnabled() && !iThreePidProvider.isLocal()
- }
- }).collect(Collectors.toList())
- }
-
- private static SingleLookupRequest build(String medium, String address) {
- SingleLookupRequest req = new SingleLookupRequest();
- req.setType(medium)
- req.setThreePid(address)
- req.setRequester("Internal")
- return req;
- }
-
- @Override
- Optional find(String medium, String address, boolean recursive) {
- return find(build(medium, address), recursive)
- }
-
- @Override
- Optional findLocal(String medium, String address) {
- return find(build(medium, address), getLocalProviders())
- }
-
- @Override
- Optional findRemote(String medium, String address) {
- return find(build(medium, address), getRemoteProviders())
- }
-
- Optional find(SingleLookupRequest request, boolean forceRecursive) {
- return find(request, listUsableProviders(request, forceRecursive));
- }
-
- Optional find(SingleLookupRequest request, List providers) {
- for (IThreePidProvider provider : providers) {
- Optional lookupDataOpt = provider.find(request)
- if (lookupDataOpt.isPresent()) {
- return lookupDataOpt
- }
- }
-
- if (
- recursiveCfg.getBridge() != null &&
- recursiveCfg.getBridge().getEnabled() &&
- (!recursiveCfg.getBridge().getRecursiveOnly() || isAllowedForRecursive(request.getRequester()))
- ) {
- log.info("Using bridge failover for lookup")
- return bridge.find(request)
- }
-
- return Optional.empty()
- }
-
- @Override
- Optional find(SingleLookupRequest request) {
- return find(request, false)
- }
-
- @Override
- Optional findRecursive(SingleLookupRequest request) {
- return find(request, true)
- }
-
- @Override
- List find(BulkLookupRequest request) {
- List mapToDo = new ArrayList<>(request.getMappings())
- List mapFoundAll = new ArrayList<>()
-
- for (IThreePidProvider provider : listUsableProviders(request)) {
- if (mapToDo.isEmpty()) {
- log.info("No more mappings to lookup")
- break
- } else {
- log.info("{} mappings remaining overall", mapToDo.size())
- }
-
- log.info("Using provider {} for remaining mappings", provider.getClass().getSimpleName())
- List mapFound = provider.populate(mapToDo)
- log.info("Provider {} returned {} mappings", provider.getClass().getSimpleName(), mapFound.size())
- mapFoundAll.addAll(mapFound)
- mapToDo.removeAll(mapFound)
- }
-
- return mapFoundAll
- }
-
-}
diff --git a/src/main/groovy/io/kamax/mxisd/signature/SignatureManager.groovy b/src/main/groovy/io/kamax/mxisd/signature/SignatureManager.groovy
deleted file mode 100644
index 84c4ad9..0000000
--- a/src/main/groovy/io/kamax/mxisd/signature/SignatureManager.groovy
+++ /dev/null
@@ -1,78 +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 .
- */
-
-package io.kamax.mxisd.signature
-
-import com.google.gson.JsonObject
-import io.kamax.mxisd.config.ServerConfig
-import io.kamax.mxisd.key.KeyManager
-import net.i2p.crypto.eddsa.EdDSAEngine
-import org.json.JSONObject
-import org.springframework.beans.factory.InitializingBean
-import org.springframework.beans.factory.annotation.Autowired
-import org.springframework.stereotype.Component
-
-import java.security.MessageDigest
-
-@Component
-class SignatureManager implements InitializingBean {
-
- @Autowired
- private KeyManager keyMgr
-
- @Autowired
- private ServerConfig srvCfg
-
- private EdDSAEngine signEngine
-
- private String sign(String message) {
- byte[] signRaw = signEngine.signOneShot(message.getBytes())
- return Base64.getEncoder().encodeToString(signRaw)
- }
-
- JSONObject signMessageJson(String message) {
- String sign = sign(message)
-
- JSONObject keySignature = new JSONObject()
- keySignature.put("ed25519:${keyMgr.getCurrentIndex()}", sign)
- JSONObject signature = new JSONObject()
- signature.put("${srvCfg.getName()}", keySignature)
-
- return signature
- }
-
- 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
- }
-
- @Override
- void afterPropertiesSet() throws Exception {
- signEngine = new EdDSAEngine(MessageDigest.getInstance(keyMgr.getSpecs().getHashAlgorithm()))
- signEngine.initSign(keyMgr.getPrivateKey(keyMgr.getCurrentIndex()))
- }
-
-}
diff --git a/src/main/groovy/io/kamax/mxisd/MatrixIdentityServerApplication.groovy b/src/main/java/io/kamax/mxisd/MatrixIdentityServerApplication.java
similarity index 87%
rename from src/main/groovy/io/kamax/mxisd/MatrixIdentityServerApplication.groovy
rename to src/main/java/io/kamax/mxisd/MatrixIdentityServerApplication.java
index a83015b..4de33e6 100644
--- a/src/main/groovy/io/kamax/mxisd/MatrixIdentityServerApplication.groovy
+++ b/src/main/java/io/kamax/mxisd/MatrixIdentityServerApplication.java
@@ -18,16 +18,16 @@
* along with this program. If not, see .
*/
-package io.kamax.mxisd
+package io.kamax.mxisd;
-import org.springframework.boot.SpringApplication
-import org.springframework.boot.autoconfigure.SpringBootApplication
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
class MatrixIdentityServerApplication {
- static void main(String[] args) throws Exception {
- SpringApplication.run(MatrixIdentityServerApplication.class, args)
+ public static void main(String[] args) {
+ SpringApplication.run(MatrixIdentityServerApplication.class, args);
}
}
diff --git a/src/main/groovy/io/kamax/mxisd/ThreePid.java b/src/main/java/io/kamax/mxisd/ThreePid.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/ThreePid.java
rename to src/main/java/io/kamax/mxisd/ThreePid.java
diff --git a/src/main/groovy/io/kamax/mxisd/UserID.java b/src/main/java/io/kamax/mxisd/UserID.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/UserID.java
rename to src/main/java/io/kamax/mxisd/UserID.java
diff --git a/src/main/groovy/io/kamax/mxisd/UserIdType.java b/src/main/java/io/kamax/mxisd/UserIdType.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/UserIdType.java
rename to src/main/java/io/kamax/mxisd/UserIdType.java
diff --git a/src/main/groovy/io/kamax/mxisd/auth/AuthManager.java b/src/main/java/io/kamax/mxisd/auth/AuthManager.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/auth/AuthManager.java
rename to src/main/java/io/kamax/mxisd/auth/AuthManager.java
diff --git a/src/main/groovy/io/kamax/mxisd/auth/UserAuthResult.java b/src/main/java/io/kamax/mxisd/auth/UserAuthResult.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/auth/UserAuthResult.java
rename to src/main/java/io/kamax/mxisd/auth/UserAuthResult.java
diff --git a/src/main/groovy/io/kamax/mxisd/auth/provider/AuthenticatorProvider.java b/src/main/java/io/kamax/mxisd/auth/provider/AuthenticatorProvider.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/auth/provider/AuthenticatorProvider.java
rename to src/main/java/io/kamax/mxisd/auth/provider/AuthenticatorProvider.java
diff --git a/src/main/groovy/io/kamax/mxisd/auth/provider/BackendAuthResult.java b/src/main/java/io/kamax/mxisd/auth/provider/BackendAuthResult.java
similarity index 86%
rename from src/main/groovy/io/kamax/mxisd/auth/provider/BackendAuthResult.java
rename to src/main/java/io/kamax/mxisd/auth/provider/BackendAuthResult.java
index 79c1036..7eb658f 100644
--- a/src/main/groovy/io/kamax/mxisd/auth/provider/BackendAuthResult.java
+++ b/src/main/java/io/kamax/mxisd/auth/provider/BackendAuthResult.java
@@ -49,20 +49,27 @@ public class BackendAuthResult {
return r;
}
+ public void fail() {
+ success = false;
+ }
+
public static BackendAuthResult success(String id, UserIdType type, String displayName) {
return success(id, type.getId(), displayName);
}
public static BackendAuthResult success(String id, String type, String displayName) {
BackendAuthResult r = new BackendAuthResult();
- r.success = true;
- r.id = new UserID(type, id);
- r.profile = new BackendAuthProfile();
- r.profile.displayName = displayName;
-
+ r.succeed(id, type, displayName);
return r;
}
+ public void succeed(String id, String type, String displayName) {
+ this.success = true;
+ this.id = new UserID(type, id);
+ this.profile = new BackendAuthProfile();
+ this.profile.displayName = displayName;
+ }
+
private Boolean success;
private UserID id;
private BackendAuthProfile profile = new BackendAuthProfile();
diff --git a/src/main/java/io/kamax/mxisd/backend/firebase/GoogleFirebaseAuthenticator.java b/src/main/java/io/kamax/mxisd/backend/firebase/GoogleFirebaseAuthenticator.java
new file mode 100644
index 0000000..00bcd66
--- /dev/null
+++ b/src/main/java/io/kamax/mxisd/backend/firebase/GoogleFirebaseAuthenticator.java
@@ -0,0 +1,177 @@
+/*
+ * 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 .
+ */
+
+package io.kamax.mxisd.backend.firebase;
+
+import com.google.firebase.FirebaseApp;
+import com.google.firebase.FirebaseOptions;
+import com.google.firebase.auth.FirebaseAuth;
+import com.google.firebase.auth.FirebaseCredential;
+import com.google.firebase.auth.FirebaseCredentials;
+import io.kamax.matrix.ThreePidMedium;
+import io.kamax.matrix._MatrixID;
+import io.kamax.mxisd.ThreePid;
+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 java.io.FileInputStream;
+import java.io.IOException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public class GoogleFirebaseAuthenticator implements AuthenticatorProvider {
+
+ private Logger log = LoggerFactory.getLogger(GoogleFirebaseAuthenticator.class);
+
+ private boolean isEnabled;
+ private FirebaseApp fbApp;
+ private FirebaseAuth fbAuth;
+
+ private void waitOnLatch(BackendAuthResult result, CountDownLatch l, String purpose) {
+ try {
+ l.await(30, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ log.warn("Interrupted while waiting for " + purpose);
+ result.fail();
+ }
+ }
+
+ public GoogleFirebaseAuthenticator(boolean isEnabled) {
+ this.isEnabled = isEnabled;
+ }
+
+ public GoogleFirebaseAuthenticator(String credsPath, String db) {
+ this(true);
+ try {
+ fbApp = FirebaseApp.initializeApp(getOpts(credsPath, db), "AuthenticationProvider");
+ fbAuth = FirebaseAuth.getInstance(fbApp);
+
+ log.info("Google Firebase Authentication is ready");
+ } catch (IOException e) {
+ throw new RuntimeException("Error when initializing Firebase", e);
+ }
+ }
+
+ private FirebaseCredential getCreds(String credsPath) throws IOException {
+ if (StringUtils.isNotBlank(credsPath)) {
+ return FirebaseCredentials.fromCertificate(new FileInputStream(credsPath));
+ } else {
+ return FirebaseCredentials.applicationDefault();
+ }
+ }
+
+ private FirebaseOptions getOpts(String credsPath, String db) throws IOException {
+ if (StringUtils.isBlank(db)) {
+ throw new IllegalArgumentException("Firebase database is not configured");
+ }
+
+ return new FirebaseOptions.Builder()
+ .setCredential(getCreds(credsPath))
+ .setDatabaseUrl(db)
+ .build();
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return isEnabled;
+ }
+
+ private void waitOnLatch(CountDownLatch l) {
+ try {
+ l.await(30, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ log.warn("Interrupted while waiting for Firebase auth check");
+ }
+ }
+
+ @Override
+ public BackendAuthResult authenticate(_MatrixID mxid, String password) {
+ if (!isEnabled()) {
+ throw new IllegalStateException();
+ }
+
+ log.info("Trying to authenticate {}", mxid);
+
+ final BackendAuthResult result = BackendAuthResult.failure();
+
+ String localpart = mxid.getLocalPart();
+ CountDownLatch l = new CountDownLatch(1);
+ fbAuth.verifyIdToken(password).addOnSuccessListener(token -> {
+ try {
+ if (!StringUtils.equals(localpart, token.getUid())) {
+ log.info("Failure to authenticate {}: Matrix ID localpart '{}' does not match Firebase UID '{}'", mxid, localpart, token.getUid());
+ result.fail();
+ return;
+ }
+
+ result.succeed(mxid.getId(), UserIdType.MatrixID.getId(), token.getName());
+ log.info("{} was successfully authenticated", mxid);
+ log.info("Fetching profile for {}", mxid);
+ CountDownLatch userRecordLatch = new CountDownLatch(1);
+ fbAuth.getUser(token.getUid()).addOnSuccessListener(user -> {
+ try {
+ if (StringUtils.isNotBlank(user.getEmail())) {
+ result.withThreePid(new ThreePid(ThreePidMedium.Email.getId(), user.getEmail()));
+ }
+
+ if (StringUtils.isNotBlank(user.getPhoneNumber())) {
+ result.withThreePid(new ThreePid(ThreePidMedium.PhoneNumber.getId(), user.getPhoneNumber()));
+ }
+
+ } finally {
+ userRecordLatch.countDown();
+ }
+ }).addOnFailureListener(e -> {
+ try {
+ log.warn("Unable to fetch Firebase user profile for {}", mxid);
+ result.fail();
+ } finally {
+ userRecordLatch.countDown();
+ }
+ });
+
+ waitOnLatch(result, userRecordLatch, "Firebase user profile");
+ } finally {
+ l.countDown();
+ }
+ }).addOnFailureListener(e -> {
+ try {
+ if (e instanceof IllegalArgumentException) {
+ log.info("Failure to authenticate {}: invalid firebase token", mxid);
+ } else {
+ log.info("Failure to authenticate {}: {}", mxid, e.getMessage(), e);
+ log.info("Exception", e);
+ }
+
+ result.fail();
+ } finally {
+ l.countDown();
+ }
+ });
+
+ waitOnLatch(result, l, "Firebase auth check");
+ return result;
+ }
+
+}
diff --git a/src/main/groovy/io/kamax/mxisd/backend/firebase/GoogleFirebaseProvider.groovy b/src/main/java/io/kamax/mxisd/backend/firebase/GoogleFirebaseProvider.java
similarity index 59%
rename from src/main/groovy/io/kamax/mxisd/backend/firebase/GoogleFirebaseProvider.groovy
rename to src/main/java/io/kamax/mxisd/backend/firebase/GoogleFirebaseProvider.java
index 95eac0a..e37ff21 100644
--- a/src/main/groovy/io/kamax/mxisd/backend/firebase/GoogleFirebaseProvider.groovy
+++ b/src/main/java/io/kamax/mxisd/backend/firebase/GoogleFirebaseProvider.java
@@ -18,40 +18,40 @@
* along with this program. If not, see .
*/
-package io.kamax.mxisd.backend.firebase
+package io.kamax.mxisd.backend.firebase;
-import com.google.firebase.FirebaseApp
-import com.google.firebase.FirebaseOptions
-import com.google.firebase.auth.FirebaseAuth
-import com.google.firebase.auth.FirebaseCredential
-import com.google.firebase.auth.FirebaseCredentials
-import com.google.firebase.auth.UserRecord
-import com.google.firebase.internal.NonNull
-import com.google.firebase.tasks.OnFailureListener
-import com.google.firebase.tasks.OnSuccessListener
-import io.kamax.matrix.ThreePidMedium
-import io.kamax.mxisd.lookup.SingleLookupReply
-import io.kamax.mxisd.lookup.SingleLookupRequest
-import io.kamax.mxisd.lookup.ThreePidMapping
-import io.kamax.mxisd.lookup.provider.IThreePidProvider
-import org.apache.commons.lang.StringUtils
-import org.slf4j.Logger
-import org.slf4j.LoggerFactory
+import com.google.firebase.FirebaseApp;
+import com.google.firebase.FirebaseOptions;
+import com.google.firebase.auth.FirebaseAuth;
+import com.google.firebase.auth.FirebaseCredential;
+import com.google.firebase.auth.FirebaseCredentials;
+import com.google.firebase.auth.UserRecord;
+import com.google.firebase.tasks.OnFailureListener;
+import com.google.firebase.tasks.OnSuccessListener;
+import io.kamax.matrix.MatrixID;
+import io.kamax.matrix.ThreePidMedium;
+import io.kamax.mxisd.lookup.SingleLookupReply;
+import io.kamax.mxisd.lookup.SingleLookupRequest;
+import io.kamax.mxisd.lookup.ThreePidMapping;
+import io.kamax.mxisd.lookup.provider.IThreePidProvider;
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
-import java.util.concurrent.CountDownLatch
-import java.util.concurrent.TimeUnit
-import java.util.function.Consumer
-import java.util.regex.Pattern
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
public class GoogleFirebaseProvider implements IThreePidProvider {
private Logger log = LoggerFactory.getLogger(GoogleFirebaseProvider.class);
- private static final Pattern matrixIdLaxPattern = Pattern.compile("@(.*):(.+)");
-
private boolean isEnabled;
private String domain;
- private FirebaseApp fbApp;
private FirebaseAuth fbAuth;
public GoogleFirebaseProvider(boolean isEnabled) {
@@ -61,8 +61,9 @@ public class GoogleFirebaseProvider implements IThreePidProvider {
public GoogleFirebaseProvider(String credsPath, String db, String domain) {
this(true);
this.domain = domain;
+
try {
- fbApp = FirebaseApp.initializeApp(getOpts(credsPath, db), "ThreePidProvider");
+ FirebaseApp fbApp = FirebaseApp.initializeApp(getOpts(credsPath, db), "ThreePidProvider");
fbAuth = FirebaseAuth.getInstance(fbApp);
log.info("Google Firebase Authentication is ready");
@@ -91,7 +92,7 @@ public class GoogleFirebaseProvider implements IThreePidProvider {
}
private String getMxid(UserRecord record) {
- return "@${record.getUid()}:${domain}";
+ return new MatrixID(record.getUid(), domain).getId();
}
@Override
@@ -118,71 +119,59 @@ public class GoogleFirebaseProvider implements IThreePidProvider {
}
private Optional findInternal(String medium, String address) {
- UserRecord r;
+ final UserRecord[] r = new UserRecord[1];
CountDownLatch l = new CountDownLatch(1);
- OnSuccessListener success = new OnSuccessListener() {
- @Override
- void onSuccess(UserRecord result) {
- log.info("Found 3PID match for {}:{} - UID is {}", medium, address, result.getUid())
- r = result;
- l.countDown()
- }
+ OnSuccessListener success = result -> {
+ log.info("Found 3PID match for {}:{} - UID is {}", medium, address, result.getUid());
+ r[0] = result;
+ l.countDown();
};
- OnFailureListener failure = new OnFailureListener() {
- @Override
- void onFailure(@NonNull Exception e) {
- log.info("No 3PID match for {}:{} - {}", medium, address, e.getMessage())
- r = null;
- l.countDown()
- }
+ OnFailureListener failure = e -> {
+ log.info("No 3PID match for {}:{} - {}", medium, address, e.getMessage());
+ r[0] = null;
+ l.countDown();
};
if (ThreePidMedium.Email.is(medium)) {
- log.info("Performing E-mail 3PID lookup for {}", address)
+ log.info("Performing E-mail 3PID lookup for {}", address);
fbAuth.getUserByEmail(address)
.addOnSuccessListener(success)
.addOnFailureListener(failure);
waitOnLatch(l);
} else if (ThreePidMedium.PhoneNumber.is(medium)) {
- log.info("Performing msisdn 3PID lookup for {}", address)
+ log.info("Performing msisdn 3PID lookup for {}", address);
fbAuth.getUserByPhoneNumber(address)
.addOnSuccessListener(success)
.addOnFailureListener(failure);
waitOnLatch(l);
} else {
log.info("{} is not a supported 3PID medium", medium);
- r = null;
+ r[0] = null;
}
- return Optional.ofNullable(r);
+ return Optional.ofNullable(r[0]);
}
@Override
public Optional find(SingleLookupRequest request) {
- Optional urOpt = findInternal(request.getType(), request.getThreePid())
- if (urOpt.isPresent()) {
- return Optional.of(new SingleLookupReply(request, getMxid(urOpt.get())));
- }
+ Optional urOpt = findInternal(request.getType(), request.getThreePid());
+ return urOpt.map(userRecord -> new SingleLookupReply(request, getMxid(userRecord)));
- return Optional.empty();
}
@Override
public List populate(List mappings) {
List results = new ArrayList<>();
- mappings.parallelStream().forEach(new Consumer() {
- @Override
- void accept(ThreePidMapping o) {
- Optional urOpt = findInternal(o.getMedium(), o.getValue());
- if (urOpt.isPresent()) {
- ThreePidMapping result = new ThreePidMapping();
- result.setMedium(o.getMedium())
- result.setValue(o.getValue())
- result.setMxid(getMxid(urOpt.get()))
- results.add(result)
- }
+ mappings.parallelStream().forEach(o -> {
+ Optional urOpt = findInternal(o.getMedium(), o.getValue());
+ if (urOpt.isPresent()) {
+ ThreePidMapping result = new ThreePidMapping();
+ result.setMedium(o.getMedium());
+ result.setValue(o.getValue());
+ result.setMxid(getMxid(urOpt.get()));
+ results.add(result);
}
});
return results;
diff --git a/src/main/groovy/io/kamax/mxisd/backend/ldap/LdapAuthProvider.java b/src/main/java/io/kamax/mxisd/backend/ldap/LdapAuthProvider.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/backend/ldap/LdapAuthProvider.java
rename to src/main/java/io/kamax/mxisd/backend/ldap/LdapAuthProvider.java
diff --git a/src/main/groovy/io/kamax/mxisd/backend/ldap/LdapGenericBackend.java b/src/main/java/io/kamax/mxisd/backend/ldap/LdapGenericBackend.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/backend/ldap/LdapGenericBackend.java
rename to src/main/java/io/kamax/mxisd/backend/ldap/LdapGenericBackend.java
diff --git a/src/main/java/io/kamax/mxisd/backend/ldap/LdapThreePidProvider.java b/src/main/java/io/kamax/mxisd/backend/ldap/LdapThreePidProvider.java
new file mode 100644
index 0000000..5dbf139
--- /dev/null
+++ b/src/main/java/io/kamax/mxisd/backend/ldap/LdapThreePidProvider.java
@@ -0,0 +1,174 @@
+/*
+ * 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 .
+ */
+
+package io.kamax.mxisd.backend.ldap;
+
+import io.kamax.mxisd.config.MatrixConfig;
+import io.kamax.mxisd.exception.InternalServerError;
+import io.kamax.mxisd.lookup.SingleLookupReply;
+import io.kamax.mxisd.lookup.SingleLookupRequest;
+import io.kamax.mxisd.lookup.ThreePidMapping;
+import io.kamax.mxisd.lookup.provider.IThreePidProvider;
+import org.apache.commons.lang.StringUtils;
+import org.apache.directory.api.ldap.model.cursor.CursorException;
+import org.apache.directory.api.ldap.model.cursor.CursorLdapReferralException;
+import org.apache.directory.api.ldap.model.cursor.EntryCursor;
+import org.apache.directory.api.ldap.model.entry.Attribute;
+import org.apache.directory.api.ldap.model.entry.Entry;
+import org.apache.directory.api.ldap.model.exception.LdapException;
+import org.apache.directory.api.ldap.model.message.SearchScope;
+import org.apache.directory.ldap.client.api.LdapConnection;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+@Component
+public class LdapThreePidProvider extends LdapGenericBackend implements IThreePidProvider {
+
+ public static final String UID = "uid";
+ public static final String MATRIX_ID = "mxid";
+
+ private Logger log = LoggerFactory.getLogger(LdapThreePidProvider.class);
+
+ @Autowired
+ private MatrixConfig mxCfg;
+
+ @Override
+ public boolean isEnabled() {
+ return getCfg().isEnabled();
+ }
+
+ private String getUidAttribute() {
+ return getCfg().getAttribute().getUid().getValue();
+ }
+
+ @Override
+ public boolean isLocal() {
+ return true;
+ }
+
+ @Override
+ public int getPriority() {
+ return 20;
+ }
+
+ private Optional lookup(LdapConnection conn, String medium, String value) {
+ String uidAttribute = getUidAttribute();
+
+ Optional queryOpt = getCfg().getIdentity().getQuery(medium);
+ if (!queryOpt.isPresent()) {
+ log.warn("{} is not a configured 3PID type for LDAP lookup", medium);
+ return Optional.empty();
+ }
+
+ String searchQuery = queryOpt.get().replaceAll("%3pid", value);
+ try (EntryCursor cursor = conn.search(getCfg().getConn().getBaseDn(), searchQuery, SearchScope.SUBTREE, uidAttribute)) {
+ while (cursor.next()) {
+ Entry entry = cursor.get();
+ log.info("Found possible match, DN: {}", entry.getDn().getName());
+
+ Attribute attribute = entry.get(uidAttribute);
+ if (attribute == null) {
+ log.info("DN {}: no attribute {}, skpping", entry.getDn(), getCfg().getAttribute());
+ continue;
+ }
+
+ String data = attribute.get().toString();
+ if (data.length() < 1) {
+ log.info("DN {}: empty attribute {}, skipping", getCfg().getAttribute());
+ continue;
+ }
+
+ StringBuilder matrixId = new StringBuilder();
+ // TODO Should we turn this block into a map of functions?
+ String uidType = getCfg().getAttribute().getUid().getType();
+ if (StringUtils.equals(UID, uidType)) {
+ matrixId.append("@").append(data).append(":").append(mxCfg.getDomain());
+ } else if (StringUtils.equals(MATRIX_ID, uidType)) {
+ matrixId.append(data);
+ } else {
+ log.warn("Bind was found but type {} is not supported", uidType);
+ continue;
+ }
+
+ log.info("DN {} is a valid match", entry.getDn().getName());
+ return Optional.of(matrixId.toString());
+ }
+ } catch (CursorLdapReferralException e) {
+ log.warn("3PID {} is only available via referral, skipping", value);
+ } catch (IOException | LdapException | CursorException e) {
+ throw new InternalServerError(e);
+ }
+
+ return Optional.empty();
+ }
+
+ @Override
+ public Optional find(SingleLookupRequest request) {
+ log.info("Performing LDAP lookup ${request.getThreePid()} of type ${request.getType()}");
+
+ try (LdapConnection conn = getConn()) {
+ bind(conn);
+
+ Optional mxid = lookup(conn, request.getType(), request.getThreePid());
+ if (mxid.isPresent()) {
+ return Optional.of(new SingleLookupReply(request, mxid.get()));
+ }
+ } catch (LdapException | IOException e) {
+ throw new InternalServerError(e);
+ }
+
+ log.info("No match found");
+ return Optional.empty();
+ }
+
+ @Override
+ public List populate(List mappings) {
+ log.info("Looking up {} mappings", mappings.size());
+ List mappingsFound = new ArrayList<>();
+
+ try (LdapConnection conn = getConn()) {
+ bind(conn);
+
+ for (ThreePidMapping mapping : mappings) {
+ try {
+ Optional mxid = lookup(conn, mapping.getMedium(), mapping.getValue());
+ if (mxid.isPresent()) {
+ mapping.setMxid(mxid.get());
+ mappingsFound.add(mapping);
+ }
+ } catch (IllegalArgumentException e) {
+ log.warn("{} is not a supported 3PID type for LDAP lookup", mapping.getMedium());
+ }
+ }
+ } catch (LdapException | IOException e) {
+ throw new InternalServerError(e);
+ }
+
+ return mappingsFound;
+ }
+
+}
diff --git a/src/main/groovy/io/kamax/mxisd/backend/rest/LookupBulkResponseJson.java b/src/main/java/io/kamax/mxisd/backend/rest/LookupBulkResponseJson.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/backend/rest/LookupBulkResponseJson.java
rename to src/main/java/io/kamax/mxisd/backend/rest/LookupBulkResponseJson.java
diff --git a/src/main/groovy/io/kamax/mxisd/backend/rest/LookupSingleRequestJson.java b/src/main/java/io/kamax/mxisd/backend/rest/LookupSingleRequestJson.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/backend/rest/LookupSingleRequestJson.java
rename to src/main/java/io/kamax/mxisd/backend/rest/LookupSingleRequestJson.java
diff --git a/src/main/groovy/io/kamax/mxisd/backend/rest/LookupSingleResponseJson.java b/src/main/java/io/kamax/mxisd/backend/rest/LookupSingleResponseJson.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/backend/rest/LookupSingleResponseJson.java
rename to src/main/java/io/kamax/mxisd/backend/rest/LookupSingleResponseJson.java
diff --git a/src/main/groovy/io/kamax/mxisd/backend/rest/RestAuthProvider.java b/src/main/java/io/kamax/mxisd/backend/rest/RestAuthProvider.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/backend/rest/RestAuthProvider.java
rename to src/main/java/io/kamax/mxisd/backend/rest/RestAuthProvider.java
diff --git a/src/main/groovy/io/kamax/mxisd/backend/rest/RestAuthRequestJson.java b/src/main/java/io/kamax/mxisd/backend/rest/RestAuthRequestJson.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/backend/rest/RestAuthRequestJson.java
rename to src/main/java/io/kamax/mxisd/backend/rest/RestAuthRequestJson.java
diff --git a/src/main/groovy/io/kamax/mxisd/backend/rest/RestProvider.java b/src/main/java/io/kamax/mxisd/backend/rest/RestProvider.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/backend/rest/RestProvider.java
rename to src/main/java/io/kamax/mxisd/backend/rest/RestProvider.java
diff --git a/src/main/groovy/io/kamax/mxisd/backend/rest/RestThreePidProvider.java b/src/main/java/io/kamax/mxisd/backend/rest/RestThreePidProvider.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/backend/rest/RestThreePidProvider.java
rename to src/main/java/io/kamax/mxisd/backend/rest/RestThreePidProvider.java
diff --git a/src/main/groovy/io/kamax/mxisd/backend/sql/SqlAuthProvider.java b/src/main/java/io/kamax/mxisd/backend/sql/SqlAuthProvider.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/backend/sql/SqlAuthProvider.java
rename to src/main/java/io/kamax/mxisd/backend/sql/SqlAuthProvider.java
diff --git a/src/main/groovy/io/kamax/mxisd/backend/sql/SqlThreePidProvider.java b/src/main/java/io/kamax/mxisd/backend/sql/SqlThreePidProvider.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/backend/sql/SqlThreePidProvider.java
rename to src/main/java/io/kamax/mxisd/backend/sql/SqlThreePidProvider.java
diff --git a/src/main/groovy/io/kamax/mxisd/config/DnsOverwrite.java b/src/main/java/io/kamax/mxisd/config/DnsOverwrite.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/config/DnsOverwrite.java
rename to src/main/java/io/kamax/mxisd/config/DnsOverwrite.java
diff --git a/src/main/groovy/io/kamax/mxisd/config/DnsOverwriteEntry.java b/src/main/java/io/kamax/mxisd/config/DnsOverwriteEntry.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/config/DnsOverwriteEntry.java
rename to src/main/java/io/kamax/mxisd/config/DnsOverwriteEntry.java
diff --git a/src/main/groovy/io/kamax/mxisd/config/FirebaseConfig.java b/src/main/java/io/kamax/mxisd/config/FirebaseConfig.java
similarity index 99%
rename from src/main/groovy/io/kamax/mxisd/config/FirebaseConfig.java
rename to src/main/java/io/kamax/mxisd/config/FirebaseConfig.java
index 6dc522d..03408be 100644
--- a/src/main/groovy/io/kamax/mxisd/config/FirebaseConfig.java
+++ b/src/main/java/io/kamax/mxisd/config/FirebaseConfig.java
@@ -85,7 +85,7 @@ public class FirebaseConfig {
if (!enabled) {
return new GoogleFirebaseAuthenticator(false);
} else {
- return new GoogleFirebaseAuthenticator(credentials, database, mxCfg.getDomain());
+ return new GoogleFirebaseAuthenticator(credentials, database);
}
}
diff --git a/src/main/groovy/io/kamax/mxisd/config/ForwardConfig.groovy b/src/main/java/io/kamax/mxisd/config/ForwardConfig.java
similarity index 70%
rename from src/main/groovy/io/kamax/mxisd/config/ForwardConfig.groovy
rename to src/main/java/io/kamax/mxisd/config/ForwardConfig.java
index 2134c3a..77cc491 100644
--- a/src/main/groovy/io/kamax/mxisd/config/ForwardConfig.groovy
+++ b/src/main/java/io/kamax/mxisd/config/ForwardConfig.java
@@ -18,23 +18,26 @@
* along with this program. If not, see .
*/
-package io.kamax.mxisd.config
+package io.kamax.mxisd.config;
-import org.springframework.boot.context.properties.ConfigurationProperties
-import org.springframework.context.annotation.Configuration
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.ArrayList;
+import java.util.List;
@Configuration
@ConfigurationProperties(prefix = "forward")
-class ForwardConfig {
+public class ForwardConfig {
- private List servers = new ArrayList<>()
+ private List servers = new ArrayList<>();
- List getServers() {
- return servers
+ public List getServers() {
+ return servers;
}
- void setServers(List servers) {
- this.servers = servers
+ public void setServers(List servers) {
+ this.servers = servers;
}
}
diff --git a/src/main/groovy/io/kamax/mxisd/config/KeyConfig.groovy b/src/main/java/io/kamax/mxisd/config/KeyConfig.java
similarity index 64%
rename from src/main/groovy/io/kamax/mxisd/config/KeyConfig.groovy
rename to src/main/java/io/kamax/mxisd/config/KeyConfig.java
index 70e6fb1..3874991 100644
--- a/src/main/groovy/io/kamax/mxisd/config/KeyConfig.groovy
+++ b/src/main/java/io/kamax/mxisd/config/KeyConfig.java
@@ -18,32 +18,33 @@
* along with this program. If not, see .
*/
-package io.kamax.mxisd.config
+package io.kamax.mxisd.config;
-import io.kamax.mxisd.exception.ConfigurationException
-import org.apache.commons.lang.StringUtils
-import org.springframework.beans.factory.InitializingBean
-import org.springframework.boot.context.properties.ConfigurationProperties
-import org.springframework.context.annotation.Configuration
+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;
@Configuration
@ConfigurationProperties(prefix = "key")
-class KeyConfig implements InitializingBean {
+public class KeyConfig {
- private String path
+ private String path;
- void setPath(String path) {
- this.path = path
+ public void setPath(String path) {
+ this.path = path;
}
- String getPath() {
- return path
+ public String getPath() {
+ return path;
}
- @Override
- void afterPropertiesSet() throws Exception {
+ @PostConstruct
+ public void build() {
if (StringUtils.isBlank(getPath())) {
- throw new ConfigurationException("key.path")
+ throw new ConfigurationException("key.path");
}
}
diff --git a/src/main/groovy/io/kamax/mxisd/config/MatrixConfig.java b/src/main/java/io/kamax/mxisd/config/MatrixConfig.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/config/MatrixConfig.java
rename to src/main/java/io/kamax/mxisd/config/MatrixConfig.java
diff --git a/src/main/java/io/kamax/mxisd/config/RecursiveLookupBridgeConfig.java b/src/main/java/io/kamax/mxisd/config/RecursiveLookupBridgeConfig.java
new file mode 100644
index 0000000..b14951b
--- /dev/null
+++ b/src/main/java/io/kamax/mxisd/config/RecursiveLookupBridgeConfig.java
@@ -0,0 +1,86 @@
+/*
+ * 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 .
+ */
+
+package io.kamax.mxisd.config;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+import javax.annotation.PostConstruct;
+import java.util.HashMap;
+import java.util.Map;
+
+@Configuration
+@ConfigurationProperties(prefix = "lookup.recursive.bridge")
+public class RecursiveLookupBridgeConfig {
+
+ private Logger log = LoggerFactory.getLogger(RecursiveLookupBridgeConfig.class);
+
+ private boolean enabled;
+ private boolean recursiveOnly;
+ private String server;
+ private Map mappings = new HashMap<>();
+
+ public boolean getEnabled() {
+ return enabled;
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ public boolean getRecursiveOnly() {
+ return recursiveOnly;
+ }
+
+ public void setRecursiveOnly(boolean recursiveOnly) {
+ this.recursiveOnly = recursiveOnly;
+ }
+
+ public String getServer() {
+ return server;
+ }
+
+ public void setServer(String server) {
+ this.server = server;
+ }
+
+ public Map getMappings() {
+ return mappings;
+ }
+
+ public void setMappings(Map mappings) {
+ this.mappings = mappings;
+ }
+
+ @PostConstruct
+ public void build() {
+ log.info("--- Bridge integration lookups config ---");
+ log.info("Enabled: {}", getEnabled());
+ if (getEnabled()) {
+ log.info("Recursive only: {}", getRecursiveOnly());
+ log.info("Fallback Server: {}", getServer());
+ log.info("Mappings: {}", mappings.size());
+ }
+ }
+
+}
diff --git a/src/main/groovy/io/kamax/mxisd/config/RecursiveLookupConfig.groovy b/src/main/java/io/kamax/mxisd/config/RecursiveLookupConfig.java
similarity index 56%
rename from src/main/groovy/io/kamax/mxisd/config/RecursiveLookupConfig.groovy
rename to src/main/java/io/kamax/mxisd/config/RecursiveLookupConfig.java
index 5c6a878..d6c6c13 100644
--- a/src/main/groovy/io/kamax/mxisd/config/RecursiveLookupConfig.groovy
+++ b/src/main/java/io/kamax/mxisd/config/RecursiveLookupConfig.java
@@ -18,41 +18,43 @@
* along with this program. If not, see .
*/
-package io.kamax.mxisd.config
+package io.kamax.mxisd.config;
-import org.springframework.boot.context.properties.ConfigurationProperties
-import org.springframework.context.annotation.Configuration
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.List;
@Configuration
@ConfigurationProperties(prefix = "lookup.recursive")
-class RecursiveLookupConfig {
+public class RecursiveLookupConfig {
- private boolean enabled
- private List allowedCidr
- private RecursiveLookupBridgeConfig bridge
+ private boolean enabled;
+ private List allowedCidr;
+ private RecursiveLookupBridgeConfig bridge;
- boolean isEnabled() {
- return enabled
+ public boolean isEnabled() {
+ return enabled;
}
- void setEnabled(boolean enabled) {
- this.enabled = enabled
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
}
- List getAllowedCidr() {
- return allowedCidr
+ public List getAllowedCidr() {
+ return allowedCidr;
}
- void setAllowedCidr(List allowedCidr) {
- this.allowedCidr = allowedCidr
+ public void setAllowedCidr(List allowedCidr) {
+ this.allowedCidr = allowedCidr;
}
- RecursiveLookupBridgeConfig getBridge() {
- return bridge
+ public RecursiveLookupBridgeConfig getBridge() {
+ return bridge;
}
- void setBridge(RecursiveLookupBridgeConfig bridge) {
- this.bridge = bridge
+ public void setBridge(RecursiveLookupBridgeConfig bridge) {
+ this.bridge = bridge;
}
}
diff --git a/src/main/groovy/io/kamax/mxisd/config/SQLiteStorageConfig.java b/src/main/java/io/kamax/mxisd/config/SQLiteStorageConfig.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/config/SQLiteStorageConfig.java
rename to src/main/java/io/kamax/mxisd/config/SQLiteStorageConfig.java
diff --git a/src/main/groovy/io/kamax/mxisd/config/ServerConfig.groovy b/src/main/java/io/kamax/mxisd/config/ServerConfig.java
similarity index 57%
rename from src/main/groovy/io/kamax/mxisd/config/ServerConfig.groovy
rename to src/main/java/io/kamax/mxisd/config/ServerConfig.java
index 0db57b2..15c1300 100644
--- a/src/main/groovy/io/kamax/mxisd/config/ServerConfig.groovy
+++ b/src/main/java/io/kamax/mxisd/config/ServerConfig.java
@@ -18,56 +18,59 @@
* along with this program. If not, see .
*/
-package io.kamax.mxisd.config
+package io.kamax.mxisd.config;
-import org.apache.commons.lang.StringUtils
-import org.slf4j.Logger
-import org.slf4j.LoggerFactory
-import org.springframework.beans.factory.InitializingBean
-import org.springframework.beans.factory.annotation.Autowired
-import org.springframework.boot.context.properties.ConfigurationProperties
-import org.springframework.context.annotation.Configuration
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+import javax.annotation.PostConstruct;
+import java.net.MalformedURLException;
+import java.net.URL;
@Configuration
@ConfigurationProperties(prefix = "server")
-class ServerConfig implements InitializingBean {
+public class ServerConfig {
private Logger log = LoggerFactory.getLogger(ServerConfig.class);
@Autowired
private MatrixConfig mxCfg;
- private String name
- private int port
- private String publicUrl
+ private String name;
+ private int port;
+ private String publicUrl;
- String getName() {
- return name
+ public String getName() {
+ return name;
}
- void setName(String name) {
- this.name = name
+ public void setName(String name) {
+ this.name = name;
}
- int getPort() {
- return port
+ public int getPort() {
+ return port;
}
- void setPort(int port) {
- this.port = port
+ public void setPort(int port) {
+ this.port = port;
}
- String getPublicUrl() {
- return publicUrl
+ public String getPublicUrl() {
+ return publicUrl;
}
- void setPublicUrl(String publicUrl) {
- this.publicUrl = publicUrl
+ public void setPublicUrl(String publicUrl) {
+ this.publicUrl = publicUrl;
}
- @Override
- void afterPropertiesSet() throws Exception {
- log.info("--- Server config ---")
+ @PostConstruct
+ public void build() {
+ log.info("--- Server config ---");
if (StringUtils.isBlank(getName())) {
setName(mxCfg.getDomain());
@@ -75,21 +78,21 @@ class ServerConfig implements InitializingBean {
}
if (StringUtils.isBlank(getPublicUrl())) {
- setPublicUrl("https://${getName()}");
+ setPublicUrl("https://" + getName());
log.debug("Public URL is empty, generating from name");
} else {
setPublicUrl(StringUtils.replace(getPublicUrl(), "%SERVER_NAME%", getName()));
}
try {
- new URL(getPublicUrl())
+ new URL(getPublicUrl());
} catch (MalformedURLException e) {
- log.warn("Public URL is not valid: {}", StringUtils.defaultIfBlank(e.getMessage(), ""))
+ log.warn("Public URL is not valid: {}", StringUtils.defaultIfBlank(e.getMessage(), ""));
}
- log.info("Name: {}", getName())
- log.info("Port: {}", getPort())
- log.info("Public URL: {}", getPublicUrl())
+ log.info("Name: {}", getName());
+ log.info("Port: {}", getPort());
+ log.info("Public URL: {}", getPublicUrl());
}
}
diff --git a/src/main/groovy/io/kamax/mxisd/config/SessionConfig.java b/src/main/java/io/kamax/mxisd/config/SessionConfig.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/config/SessionConfig.java
rename to src/main/java/io/kamax/mxisd/config/SessionConfig.java
diff --git a/src/main/groovy/io/kamax/mxisd/config/StorageConfig.java b/src/main/java/io/kamax/mxisd/config/StorageConfig.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/config/StorageConfig.java
rename to src/main/java/io/kamax/mxisd/config/StorageConfig.java
diff --git a/src/main/groovy/io/kamax/mxisd/config/ThymeleafConfig.java b/src/main/java/io/kamax/mxisd/config/ThymeleafConfig.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/config/ThymeleafConfig.java
rename to src/main/java/io/kamax/mxisd/config/ThymeleafConfig.java
diff --git a/src/main/groovy/io/kamax/mxisd/config/ViewConfig.java b/src/main/java/io/kamax/mxisd/config/ViewConfig.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/config/ViewConfig.java
rename to src/main/java/io/kamax/mxisd/config/ViewConfig.java
diff --git a/src/main/groovy/io/kamax/mxisd/config/ldap/LdapAttributeConfig.java b/src/main/java/io/kamax/mxisd/config/ldap/LdapAttributeConfig.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/config/ldap/LdapAttributeConfig.java
rename to src/main/java/io/kamax/mxisd/config/ldap/LdapAttributeConfig.java
diff --git a/src/main/groovy/io/kamax/mxisd/config/ldap/LdapAttributeUidConfig.java b/src/main/java/io/kamax/mxisd/config/ldap/LdapAttributeUidConfig.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/config/ldap/LdapAttributeUidConfig.java
rename to src/main/java/io/kamax/mxisd/config/ldap/LdapAttributeUidConfig.java
diff --git a/src/main/groovy/io/kamax/mxisd/config/ldap/LdapAuthConfig.java b/src/main/java/io/kamax/mxisd/config/ldap/LdapAuthConfig.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/config/ldap/LdapAuthConfig.java
rename to src/main/java/io/kamax/mxisd/config/ldap/LdapAuthConfig.java
diff --git a/src/main/java/io/kamax/mxisd/config/ldap/LdapConfig.java b/src/main/java/io/kamax/mxisd/config/ldap/LdapConfig.java
new file mode 100644
index 0000000..fa9c72c
--- /dev/null
+++ b/src/main/java/io/kamax/mxisd/config/ldap/LdapConfig.java
@@ -0,0 +1,131 @@
+/*
+ * 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 .
+ */
+
+package io.kamax.mxisd.config.ldap;
+
+import com.google.gson.Gson;
+import io.kamax.mxisd.backend.ldap.LdapThreePidProvider;
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+import javax.annotation.PostConstruct;
+
+@Configuration
+@ConfigurationProperties(prefix = "ldap")
+public class LdapConfig {
+
+ private static Gson gson = new Gson();
+
+ private Logger log = LoggerFactory.getLogger(LdapConfig.class);
+
+ private boolean enabled;
+
+ @Autowired
+ private LdapConnectionConfig conn;
+ private LdapAttributeConfig attribute;
+ private LdapAuthConfig auth;
+ private LdapIdentityConfig identity;
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ public LdapConnectionConfig getConn() {
+ return conn;
+ }
+
+ public void setConn(LdapConnectionConfig conn) {
+ this.conn = conn;
+ }
+
+ public LdapAttributeConfig getAttribute() {
+ return attribute;
+ }
+
+ public void setAttribute(LdapAttributeConfig attribute) {
+ this.attribute = attribute;
+ }
+
+ public LdapAuthConfig getAuth() {
+ return auth;
+ }
+
+ public void setAuth(LdapAuthConfig auth) {
+ this.auth = auth;
+ }
+
+ public LdapIdentityConfig getIdentity() {
+ return identity;
+ }
+
+ public void setIdentity(LdapIdentityConfig identity) {
+ this.identity = identity;
+ }
+
+ @PostConstruct
+ public void build() {
+ log.info("--- LDAP Config ---");
+ log.info("Enabled: {}", isEnabled());
+
+ if (!isEnabled()) {
+ return;
+ }
+
+ if (StringUtils.isBlank(conn.getHost())) {
+ throw new IllegalStateException("LDAP Host must be configured!");
+ }
+
+ if (1 > conn.getPort() || 65535 < conn.getPort()) {
+ throw new IllegalStateException("LDAP port is not valid");
+ }
+
+ if (StringUtils.isBlank(attribute.getUid().getType())) {
+ throw new IllegalStateException("Attribute UID Type cannot be empty");
+ }
+
+
+ if (StringUtils.isBlank(attribute.getUid().getValue())) {
+ throw new IllegalStateException("Attribute UID value cannot be empty");
+ }
+
+ String uidType = attribute.getUid().getType();
+ if (!StringUtils.equals(LdapThreePidProvider.UID, uidType) && !StringUtils.equals(LdapThreePidProvider.MATRIX_ID, uidType)) {
+ throw new IllegalArgumentException("Unsupported LDAP UID type: " + uidType);
+ }
+
+ log.info("Host: {}", conn.getHost());
+ log.info("Port: {}", conn.getPort());
+ log.info("Bind DN: {}", conn.getBindDn());
+ log.info("Base DN: {}", conn.getBaseDn());
+
+ log.info("Attribute: {}", gson.toJson(attribute));
+ log.info("Auth: {}", gson.toJson(auth));
+ log.info("Identity: {}", gson.toJson(identity));
+ }
+
+}
diff --git a/src/main/groovy/io/kamax/mxisd/config/ldap/LdapConnectionConfig.java b/src/main/java/io/kamax/mxisd/config/ldap/LdapConnectionConfig.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/config/ldap/LdapConnectionConfig.java
rename to src/main/java/io/kamax/mxisd/config/ldap/LdapConnectionConfig.java
diff --git a/src/main/groovy/io/kamax/mxisd/config/ldap/LdapIdentityConfig.java b/src/main/java/io/kamax/mxisd/config/ldap/LdapIdentityConfig.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/config/ldap/LdapIdentityConfig.java
rename to src/main/java/io/kamax/mxisd/config/ldap/LdapIdentityConfig.java
diff --git a/src/main/groovy/io/kamax/mxisd/config/rest/RestBackendConfig.java b/src/main/java/io/kamax/mxisd/config/rest/RestBackendConfig.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/config/rest/RestBackendConfig.java
rename to src/main/java/io/kamax/mxisd/config/rest/RestBackendConfig.java
diff --git a/src/main/groovy/io/kamax/mxisd/config/sql/SqlProviderAuthConfig.java b/src/main/java/io/kamax/mxisd/config/sql/SqlProviderAuthConfig.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/config/sql/SqlProviderAuthConfig.java
rename to src/main/java/io/kamax/mxisd/config/sql/SqlProviderAuthConfig.java
diff --git a/src/main/groovy/io/kamax/mxisd/config/sql/SqlProviderConfig.java b/src/main/java/io/kamax/mxisd/config/sql/SqlProviderConfig.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/config/sql/SqlProviderConfig.java
rename to src/main/java/io/kamax/mxisd/config/sql/SqlProviderConfig.java
diff --git a/src/main/groovy/io/kamax/mxisd/config/sql/SqlProviderIdentityConfig.java b/src/main/java/io/kamax/mxisd/config/sql/SqlProviderIdentityConfig.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/config/sql/SqlProviderIdentityConfig.java
rename to src/main/java/io/kamax/mxisd/config/sql/SqlProviderIdentityConfig.java
diff --git a/src/main/groovy/io/kamax/mxisd/config/threepid/connector/EmailSmtpConfig.java b/src/main/java/io/kamax/mxisd/config/threepid/connector/EmailSmtpConfig.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/config/threepid/connector/EmailSmtpConfig.java
rename to src/main/java/io/kamax/mxisd/config/threepid/connector/EmailSmtpConfig.java
diff --git a/src/main/groovy/io/kamax/mxisd/config/threepid/medium/EmailConfig.java b/src/main/java/io/kamax/mxisd/config/threepid/medium/EmailConfig.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/config/threepid/medium/EmailConfig.java
rename to src/main/java/io/kamax/mxisd/config/threepid/medium/EmailConfig.java
diff --git a/src/main/groovy/io/kamax/mxisd/config/threepid/medium/EmailTemplateConfig.java b/src/main/java/io/kamax/mxisd/config/threepid/medium/EmailTemplateConfig.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/config/threepid/medium/EmailTemplateConfig.java
rename to src/main/java/io/kamax/mxisd/config/threepid/medium/EmailTemplateConfig.java
diff --git a/src/main/groovy/io/kamax/mxisd/controller/v1/AuthController.java b/src/main/java/io/kamax/mxisd/controller/v1/AuthController.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/controller/v1/AuthController.java
rename to src/main/java/io/kamax/mxisd/controller/v1/AuthController.java
diff --git a/src/main/groovy/io/kamax/mxisd/controller/v1/ClientBulkLookupAnswer.java b/src/main/java/io/kamax/mxisd/controller/v1/ClientBulkLookupAnswer.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/controller/v1/ClientBulkLookupAnswer.java
rename to src/main/java/io/kamax/mxisd/controller/v1/ClientBulkLookupAnswer.java
diff --git a/src/main/groovy/io/kamax/mxisd/controller/v1/ClientBulkLookupRequest.groovy b/src/main/java/io/kamax/mxisd/controller/v1/ClientBulkLookupRequest.java
similarity index 57%
rename from src/main/groovy/io/kamax/mxisd/controller/v1/ClientBulkLookupRequest.groovy
rename to src/main/java/io/kamax/mxisd/controller/v1/ClientBulkLookupRequest.java
index 7109c5c..eb7ab73 100644
--- a/src/main/groovy/io/kamax/mxisd/controller/v1/ClientBulkLookupRequest.groovy
+++ b/src/main/java/io/kamax/mxisd/controller/v1/ClientBulkLookupRequest.java
@@ -18,28 +18,31 @@
* along with this program. If not, see .
*/
-package io.kamax.mxisd.controller.v1
+package io.kamax.mxisd.controller.v1;
-import io.kamax.mxisd.lookup.ThreePidMapping
+import io.kamax.mxisd.lookup.ThreePidMapping;
-class ClientBulkLookupRequest {
+import java.util.ArrayList;
+import java.util.List;
- private List> threepids = new ArrayList<>()
+public class ClientBulkLookupRequest {
- List> getThreepids() {
- return threepids
+ private List> threepids = new ArrayList<>();
+
+ public List> getThreepids() {
+ return threepids;
}
- void setThreepids(List> threepids) {
- this.threepids = threepids
+ public void setThreepids(List> threepids) {
+ this.threepids = threepids;
}
- void setMappings(List mappings) {
+ public void setMappings(List mappings) {
for (ThreePidMapping mapping : mappings) {
- List threepid = new ArrayList<>()
- threepid.add(mapping.getMedium())
- threepid.add(mapping.getValue())
- threepids.add(threepid)
+ List threepid = new ArrayList<>();
+ threepid.add(mapping.getMedium());
+ threepid.add(mapping.getValue());
+ threepids.add(threepid);
}
}
diff --git a/src/main/groovy/io/kamax/mxisd/controller/v1/DefaultExceptionHandler.java b/src/main/java/io/kamax/mxisd/controller/v1/DefaultExceptionHandler.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/controller/v1/DefaultExceptionHandler.java
rename to src/main/java/io/kamax/mxisd/controller/v1/DefaultExceptionHandler.java
diff --git a/src/main/groovy/io/kamax/mxisd/controller/v1/IdentityAPIv1.java b/src/main/java/io/kamax/mxisd/controller/v1/IdentityAPIv1.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/controller/v1/IdentityAPIv1.java
rename to src/main/java/io/kamax/mxisd/controller/v1/IdentityAPIv1.java
diff --git a/src/main/groovy/io/kamax/mxisd/controller/v1/InvitationController.groovy b/src/main/java/io/kamax/mxisd/controller/v1/InvitationController.java
similarity index 57%
rename from src/main/groovy/io/kamax/mxisd/controller/v1/InvitationController.groovy
rename to src/main/java/io/kamax/mxisd/controller/v1/InvitationController.java
index 9683da1..fe8541d 100644
--- a/src/main/groovy/io/kamax/mxisd/controller/v1/InvitationController.groovy
+++ b/src/main/java/io/kamax/mxisd/controller/v1/InvitationController.java
@@ -18,47 +18,49 @@
* along with this program. If not, see .
*/
-package io.kamax.mxisd.controller.v1
+package io.kamax.mxisd.controller.v1;
-import com.google.gson.Gson
-import io.kamax.matrix.MatrixID
-import io.kamax.mxisd.config.ServerConfig
-import io.kamax.mxisd.controller.v1.io.ThreePidInviteReplyIO
-import io.kamax.mxisd.invitation.IThreePidInvite
-import io.kamax.mxisd.invitation.IThreePidInviteReply
-import io.kamax.mxisd.invitation.InvitationManager
-import io.kamax.mxisd.invitation.ThreePidInvite
-import io.kamax.mxisd.key.KeyManager
-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.RequestMapping
-import org.springframework.web.bind.annotation.RequestParam
-import org.springframework.web.bind.annotation.RestController
+import com.google.gson.Gson;
+import io.kamax.matrix.MatrixID;
+import io.kamax.mxisd.config.ServerConfig;
+import io.kamax.mxisd.controller.v1.io.ThreePidInviteReplyIO;
+import io.kamax.mxisd.invitation.IThreePidInvite;
+import io.kamax.mxisd.invitation.IThreePidInviteReply;
+import io.kamax.mxisd.invitation.InvitationManager;
+import io.kamax.mxisd.invitation.ThreePidInvite;
+import io.kamax.mxisd.key.KeyManager;
+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.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
-import javax.servlet.http.HttpServletRequest
+import javax.servlet.http.HttpServletRequest;
+import java.util.HashMap;
+import java.util.Map;
-import static org.springframework.web.bind.annotation.RequestMethod.POST
+import static org.springframework.web.bind.annotation.RequestMethod.POST;
@RestController
@CrossOrigin
@RequestMapping(path = IdentityAPIv1.BASE, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
class InvitationController {
- private Logger log = LoggerFactory.getLogger(InvitationController.class)
+ private Logger log = LoggerFactory.getLogger(InvitationController.class);
@Autowired
- private InvitationManager mgr
+ private InvitationManager mgr;
@Autowired
- private KeyManager keyMgr
+ private KeyManager keyMgr;
@Autowired
- private ServerConfig srvCfg
+ private ServerConfig srvCfg;
- private Gson gson = new Gson()
+ private Gson gson = new Gson();
@RequestMapping(value = "/store-invite", method = POST)
String store(
@@ -67,14 +69,14 @@ class InvitationController {
@RequestParam String medium,
@RequestParam String address,
@RequestParam("room_id") String roomId) {
- Map parameters = new HashMap<>()
+ Map parameters = new HashMap<>();
for (String key : request.getParameterMap().keySet()) {
parameters.put(key, request.getParameter(key));
}
- IThreePidInvite invite = new ThreePidInvite(new MatrixID(sender), medium, address, roomId, parameters)
- IThreePidInviteReply reply = mgr.storeInvite(invite)
+ IThreePidInvite invite = new ThreePidInvite(new MatrixID(sender), medium, address, roomId, parameters);
+ IThreePidInviteReply reply = mgr.storeInvite(invite);
- return gson.toJson(new ThreePidInviteReplyIO(reply, keyMgr.getPublicKeyBase64(keyMgr.getCurrentIndex()), srvCfg.getPublicUrl()))
+ return gson.toJson(new ThreePidInviteReplyIO(reply, keyMgr.getPublicKeyBase64(keyMgr.getCurrentIndex()), srvCfg.getPublicUrl()));
}
}
diff --git a/src/main/groovy/io/kamax/mxisd/controller/v1/KeyController.groovy b/src/main/java/io/kamax/mxisd/controller/v1/KeyController.java
similarity index 56%
rename from src/main/groovy/io/kamax/mxisd/controller/v1/KeyController.groovy
rename to src/main/java/io/kamax/mxisd/controller/v1/KeyController.java
index 6a4e7f1..bac51af 100644
--- a/src/main/groovy/io/kamax/mxisd/controller/v1/KeyController.groovy
+++ b/src/main/java/io/kamax/mxisd/controller/v1/KeyController.java
@@ -18,64 +18,64 @@
* along with this program. If not, see .
*/
-package io.kamax.mxisd.controller.v1
+package io.kamax.mxisd.controller.v1;
-import com.google.gson.Gson
-import groovy.json.JsonOutput
-import io.kamax.mxisd.controller.v1.io.KeyValidityJson
-import io.kamax.mxisd.exception.BadRequestException
-import io.kamax.mxisd.key.KeyManager
-import org.apache.commons.lang.StringUtils
-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.*
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+import io.kamax.mxisd.controller.v1.io.KeyValidityJson;
+import io.kamax.mxisd.exception.BadRequestException;
+import io.kamax.mxisd.key.KeyManager;
+import org.apache.commons.lang.StringUtils;
+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.*;
-import javax.servlet.http.HttpServletRequest
+import javax.servlet.http.HttpServletRequest;
-import static org.springframework.web.bind.annotation.RequestMethod.GET
+import static org.springframework.web.bind.annotation.RequestMethod.GET;
@RestController
@CrossOrigin
@RequestMapping(path = IdentityAPIv1.BASE, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
-class KeyController {
+public class KeyController {
- private Logger log = LoggerFactory.getLogger(KeyController.class)
+ private Logger log = LoggerFactory.getLogger(KeyController.class);
@Autowired
- private KeyManager keyMgr
+ private KeyManager keyMgr;
private Gson gson = new Gson();
private String validKey = gson.toJson(new KeyValidityJson(true));
private String invalidKey = gson.toJson(new KeyValidityJson(false));
@RequestMapping(value = "/pubkey/{keyType}:{keyId}", method = GET)
- String getKey(@PathVariable String keyType, @PathVariable int keyId) {
+ public String getKey(@PathVariable String keyType, @PathVariable int keyId) {
if (!"ed25519".contentEquals(keyType)) {
- throw new BadRequestException("Invalid algorithm: " + keyType)
+ throw new BadRequestException("Invalid algorithm: " + keyType);
}
- log.info("Key {}:{} was requested", keyType, keyId)
- return JsonOutput.toJson([
- public_key: keyMgr.getPublicKeyBase64(keyId)
- ])
+ log.info("Key {}:{} was requested", keyType, keyId);
+ JsonObject obj = new JsonObject();
+ obj.addProperty("public_key", keyMgr.getPublicKeyBase64(keyId));
+ return gson.toJson(obj);
}
@RequestMapping(value = "/pubkey/ephemeral/isvalid", method = GET)
- String checkEphemeralKeyValidity(HttpServletRequest request) {
- log.warn("Ephemeral key was request but no ephemeral key are generated, replying not valid")
+ public String checkEphemeralKeyValidity(HttpServletRequest request) {
+ log.warn("Ephemeral key was request but no ephemeral key are generated, replying not valid");
- return invalidKey
+ return invalidKey;
}
@RequestMapping(value = "/pubkey/isvalid", method = GET)
- String checkKeyValidity(HttpServletRequest request, @RequestParam("public_key") String pubKey) {
- log.info("Validating public key {}", pubKey)
+ public String checkKeyValidity(HttpServletRequest request, @RequestParam("public_key") String pubKey) {
+ log.info("Validating public key {}", pubKey);
// TODO do in manager
- boolean valid = StringUtils.equals(pubKey, keyMgr.getPublicKeyBase64(keyMgr.getCurrentIndex()))
- return valid ? validKey : invalidKey
+ boolean valid = StringUtils.equals(pubKey, keyMgr.getPublicKeyBase64(keyMgr.getCurrentIndex()));
+ return valid ? validKey : invalidKey;
}
}
diff --git a/src/main/java/io/kamax/mxisd/controller/v1/MappingController.java b/src/main/java/io/kamax/mxisd/controller/v1/MappingController.java
new file mode 100644
index 0000000..c665729
--- /dev/null
+++ b/src/main/java/io/kamax/mxisd/controller/v1/MappingController.java
@@ -0,0 +1,130 @@
+/*
+ * 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 .
+ */
+
+package io.kamax.mxisd.controller.v1;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+import io.kamax.mxisd.controller.v1.io.SingeLookupReplyJson;
+import io.kamax.mxisd.exception.InternalServerError;
+import io.kamax.mxisd.lookup.*;
+import io.kamax.mxisd.lookup.strategy.LookupStrategy;
+import io.kamax.mxisd.signature.SignatureManager;
+import io.kamax.mxisd.util.GsonParser;
+import org.apache.commons.lang.StringUtils;
+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.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.servlet.http.HttpServletRequest;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
+
+import static org.springframework.web.bind.annotation.RequestMethod.GET;
+import static org.springframework.web.bind.annotation.RequestMethod.POST;
+
+@RestController
+@CrossOrigin
+@RequestMapping(path = IdentityAPIv1.BASE, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
+public class MappingController {
+
+ private Logger log = LoggerFactory.getLogger(MappingController.class);
+ private Gson gson = new Gson();
+ private GsonParser parser = new GsonParser(gson);
+
+ @Autowired
+ private LookupStrategy strategy;
+
+ @Autowired
+ private SignatureManager signMgr;
+
+ private void setRequesterInfo(ALookupRequest lookupReq, HttpServletRequest req) {
+ lookupReq.setRequester(req.getRemoteAddr());
+ String xff = req.getHeader("X-FORWARDED-FOR");
+ lookupReq.setRecursive(StringUtils.isNotBlank(xff));
+ if (lookupReq.isRecursive()) {
+ lookupReq.setRecurseHosts(Arrays.asList(xff.split(",")));
+ }
+
+ lookupReq.setUserAgent(req.getHeader("USER-AGENT"));
+ }
+
+ @RequestMapping(value = "/lookup", method = GET)
+ String lookup(HttpServletRequest request, @RequestParam String medium, @RequestParam String address) {
+ SingleLookupRequest lookupRequest = new SingleLookupRequest();
+ setRequesterInfo(lookupRequest, request);
+ lookupRequest.setType(medium);
+ lookupRequest.setThreePid(address);
+
+ log.info("Got single lookup request from {} with client {} - Is recursive? {}", lookupRequest.getRequester(), lookupRequest.getUserAgent(), lookupRequest.isRecursive());
+
+ Optional lookupOpt = strategy.find(lookupRequest);
+ if (!lookupOpt.isPresent()) {
+ log.info("No mapping was found, return empty JSON object");
+ return "{}";
+ }
+
+ 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);
+ }
+ }
+
+ @RequestMapping(value = "/bulk_lookup", method = POST)
+ String bulkLookup(HttpServletRequest request) {
+ BulkLookupRequest lookupRequest = new BulkLookupRequest();
+ setRequesterInfo(lookupRequest, request);
+ log.info("Got single lookup request from {} with client {} - Is recursive? {}", lookupRequest.getRequester(), lookupRequest.getUserAgent(), lookupRequest.isRecursive());
+
+ try {
+ ClientBulkLookupRequest input = parser.parse(request, ClientBulkLookupRequest.class);
+ List mappings = new ArrayList<>();
+ for (List mappingRaw : input.getThreepids()) {
+ ThreePidMapping mapping = new ThreePidMapping();
+ mapping.setMedium(mappingRaw.get(0));
+ mapping.setValue(mappingRaw.get(1));
+ mappings.add(mapping);
+ }
+ lookupRequest.setMappings(mappings);
+
+ ClientBulkLookupAnswer answer = new ClientBulkLookupAnswer();
+ answer.addAll(strategy.find(lookupRequest));
+ return gson.toJson(answer);
+ } catch (IOException e) {
+ throw new InternalServerError(e);
+ }
+ }
+
+}
diff --git a/src/main/groovy/io/kamax/mxisd/controller/v1/SessionController.groovy b/src/main/java/io/kamax/mxisd/controller/v1/SessionController.java
similarity index 58%
rename from src/main/groovy/io/kamax/mxisd/controller/v1/SessionController.groovy
rename to src/main/java/io/kamax/mxisd/controller/v1/SessionController.java
index 718d671..03ca111 100644
--- a/src/main/groovy/io/kamax/mxisd/controller/v1/SessionController.groovy
+++ b/src/main/java/io/kamax/mxisd/controller/v1/SessionController.java
@@ -18,41 +18,44 @@
* along with this program. If not, see .
*/
-package io.kamax.mxisd.controller.v1
+package io.kamax.mxisd.controller.v1;
-import io.kamax.mxisd.config.ServerConfig
-import io.kamax.mxisd.config.ViewConfig
-import io.kamax.mxisd.controller.v1.remote.RemoteIdentityAPIv1
-import io.kamax.mxisd.session.SessionMananger
-import io.kamax.mxisd.session.ValidationResult
-import org.slf4j.Logger
-import org.slf4j.LoggerFactory
-import org.springframework.beans.factory.annotation.Autowired
-import org.springframework.stereotype.Controller
-import org.springframework.ui.Model
-import org.springframework.web.bind.annotation.RequestMapping
-import org.springframework.web.bind.annotation.RequestParam
+import io.kamax.mxisd.config.ServerConfig;
+import io.kamax.mxisd.config.ViewConfig;
+import io.kamax.mxisd.controller.v1.remote.RemoteIdentityAPIv1;
+import io.kamax.mxisd.exception.InternalServerError;
+import io.kamax.mxisd.session.SessionMananger;
+import io.kamax.mxisd.session.ValidationResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
-import javax.servlet.http.HttpServletRequest
-import javax.servlet.http.HttpServletResponse
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
@Controller
@RequestMapping(path = IdentityAPIv1.BASE)
class SessionController {
- private Logger log = LoggerFactory.getLogger(SessionController.class)
+ private Logger log = LoggerFactory.getLogger(SessionController.class);
@Autowired
private ServerConfig srvCfg;
@Autowired
- private SessionMananger mgr
+ private SessionMananger mgr;
@Autowired
private ViewConfig viewCfg;
+ ;
@RequestMapping(value = "/validate/{medium}/submitToken")
- String validate(
+ public String validate(
HttpServletRequest request,
HttpServletResponse response,
@RequestParam String sid,
@@ -60,21 +63,27 @@ class SessionController {
@RequestParam String token,
Model model
) {
- log.info("Requested: {}?{}", request.getRequestURL(), request.getQueryString())
+ log.info("Requested: {}?{}", request.getRequestURL(), request.getQueryString());
- ValidationResult r = mgr.validate(sid, secret, token)
- log.info("Session {} was validated", sid)
+ ValidationResult r = mgr.validate(sid, secret, token);
+ log.info("Session {} was validated", sid);
if (r.getNextUrl().isPresent()) {
- String url = srvCfg.getPublicUrl() + r.getNextUrl().get()
- log.info("Session {} validation: next URL is present, redirecting to {}", sid, url)
- response.sendRedirect(url)
+ String url = srvCfg.getPublicUrl() + r.getNextUrl().get();
+ log.info("Session {} validation: next URL is present, redirecting to {}", sid, url);
+ try {
+ response.sendRedirect(url);
+ return "";
+ } catch (IOException e) {
+ log.warn("Unable to redirect user to {}", url);
+ throw new InternalServerError(e);
+ }
} else {
if (r.isCanRemote()) {
String url = srvCfg.getPublicUrl() + RemoteIdentityAPIv1.getRequestToken(r.getSession().getId(), r.getSession().getSecret());
- model.addAttribute("remoteSessionLink", url)
- return viewCfg.getSession().getLocalRemote().getOnTokenSubmit().getSuccess()
+ model.addAttribute("remoteSessionLink", url);
+ return viewCfg.getSession().getLocalRemote().getOnTokenSubmit().getSuccess();
} else {
- return viewCfg.getSession().getLocal().getOnTokenSubmit().getSuccess()
+ return viewCfg.getSession().getLocal().getOnTokenSubmit().getSuccess();
}
}
}
diff --git a/src/main/groovy/io/kamax/mxisd/controller/v1/SessionRestController.java b/src/main/java/io/kamax/mxisd/controller/v1/SessionRestController.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/controller/v1/SessionRestController.java
rename to src/main/java/io/kamax/mxisd/controller/v1/SessionRestController.java
diff --git a/src/main/groovy/io/kamax/mxisd/controller/v1/StatusController.java b/src/main/java/io/kamax/mxisd/controller/v1/StatusController.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/controller/v1/StatusController.java
rename to src/main/java/io/kamax/mxisd/controller/v1/StatusController.java
diff --git a/src/main/groovy/io/kamax/mxisd/controller/v1/io/GenericTokenRequestJson.java b/src/main/java/io/kamax/mxisd/controller/v1/io/GenericTokenRequestJson.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/controller/v1/io/GenericTokenRequestJson.java
rename to src/main/java/io/kamax/mxisd/controller/v1/io/GenericTokenRequestJson.java
diff --git a/src/main/groovy/io/kamax/mxisd/controller/v1/io/KeyValidityJson.java b/src/main/java/io/kamax/mxisd/controller/v1/io/KeyValidityJson.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/controller/v1/io/KeyValidityJson.java
rename to src/main/java/io/kamax/mxisd/controller/v1/io/KeyValidityJson.java
diff --git a/src/main/groovy/io/kamax/mxisd/controller/v1/io/RequestTokenResponse.java b/src/main/java/io/kamax/mxisd/controller/v1/io/RequestTokenResponse.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/controller/v1/io/RequestTokenResponse.java
rename to src/main/java/io/kamax/mxisd/controller/v1/io/RequestTokenResponse.java
diff --git a/src/main/groovy/io/kamax/mxisd/controller/v1/io/SessionEmailTokenRequestJson.java b/src/main/java/io/kamax/mxisd/controller/v1/io/SessionEmailTokenRequestJson.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/controller/v1/io/SessionEmailTokenRequestJson.java
rename to src/main/java/io/kamax/mxisd/controller/v1/io/SessionEmailTokenRequestJson.java
diff --git a/src/main/groovy/io/kamax/mxisd/controller/v1/io/SessionPhoneTokenRequestJson.java b/src/main/java/io/kamax/mxisd/controller/v1/io/SessionPhoneTokenRequestJson.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/controller/v1/io/SessionPhoneTokenRequestJson.java
rename to src/main/java/io/kamax/mxisd/controller/v1/io/SessionPhoneTokenRequestJson.java
diff --git a/src/main/groovy/io/kamax/mxisd/controller/v1/io/SingeLookupReplyJson.java b/src/main/java/io/kamax/mxisd/controller/v1/io/SingeLookupReplyJson.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/controller/v1/io/SingeLookupReplyJson.java
rename to src/main/java/io/kamax/mxisd/controller/v1/io/SingeLookupReplyJson.java
diff --git a/src/main/groovy/io/kamax/mxisd/controller/v1/io/ThreePidInviteReplyIO.java b/src/main/java/io/kamax/mxisd/controller/v1/io/ThreePidInviteReplyIO.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/controller/v1/io/ThreePidInviteReplyIO.java
rename to src/main/java/io/kamax/mxisd/controller/v1/io/ThreePidInviteReplyIO.java
diff --git a/src/main/groovy/io/kamax/mxisd/controller/v1/remote/RemoteIdentityAPIv1.java b/src/main/java/io/kamax/mxisd/controller/v1/remote/RemoteIdentityAPIv1.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/controller/v1/remote/RemoteIdentityAPIv1.java
rename to src/main/java/io/kamax/mxisd/controller/v1/remote/RemoteIdentityAPIv1.java
diff --git a/src/main/groovy/io/kamax/mxisd/controller/v1/remote/RemoteSessionController.java b/src/main/java/io/kamax/mxisd/controller/v1/remote/RemoteSessionController.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/controller/v1/remote/RemoteSessionController.java
rename to src/main/java/io/kamax/mxisd/controller/v1/remote/RemoteSessionController.java
diff --git a/src/main/groovy/io/kamax/mxisd/exception/BadRequestException.groovy b/src/main/java/io/kamax/mxisd/exception/BadRequestException.java
similarity index 76%
rename from src/main/groovy/io/kamax/mxisd/exception/BadRequestException.groovy
rename to src/main/java/io/kamax/mxisd/exception/BadRequestException.java
index a1caa26..b373e08 100644
--- a/src/main/groovy/io/kamax/mxisd/exception/BadRequestException.groovy
+++ b/src/main/java/io/kamax/mxisd/exception/BadRequestException.java
@@ -18,16 +18,16 @@
* along with this program. If not, see .
*/
-package io.kamax.mxisd.exception
+package io.kamax.mxisd.exception;
-import org.springframework.http.HttpStatus
-import org.springframework.web.bind.annotation.ResponseStatus
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(value = HttpStatus.BAD_REQUEST)
-class BadRequestException extends RuntimeException {
+public class BadRequestException extends RuntimeException {
- BadRequestException(String s) {
- super(s)
+ public BadRequestException(String s) {
+ super(s);
}
}
diff --git a/src/main/groovy/io/kamax/mxisd/exception/ConfigurationException.java b/src/main/java/io/kamax/mxisd/exception/ConfigurationException.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/exception/ConfigurationException.java
rename to src/main/java/io/kamax/mxisd/exception/ConfigurationException.java
diff --git a/src/main/groovy/io/kamax/mxisd/exception/InternalServerError.java b/src/main/java/io/kamax/mxisd/exception/InternalServerError.java
similarity index 95%
rename from src/main/groovy/io/kamax/mxisd/exception/InternalServerError.java
rename to src/main/java/io/kamax/mxisd/exception/InternalServerError.java
index 5680075..80c83ac 100644
--- a/src/main/groovy/io/kamax/mxisd/exception/InternalServerError.java
+++ b/src/main/java/io/kamax/mxisd/exception/InternalServerError.java
@@ -43,6 +43,10 @@ public class InternalServerError extends MatrixException {
this.internalReason = internalReason;
}
+ public InternalServerError(Throwable t) {
+ this(t.getMessage());
+ }
+
public String getReference() {
return reference;
}
diff --git a/src/main/groovy/io/kamax/mxisd/exception/InvalidCredentialsException.java b/src/main/java/io/kamax/mxisd/exception/InvalidCredentialsException.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/exception/InvalidCredentialsException.java
rename to src/main/java/io/kamax/mxisd/exception/InvalidCredentialsException.java
diff --git a/src/main/groovy/io/kamax/mxisd/exception/InvalidResponseJsonException.java b/src/main/java/io/kamax/mxisd/exception/InvalidResponseJsonException.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/exception/InvalidResponseJsonException.java
rename to src/main/java/io/kamax/mxisd/exception/InvalidResponseJsonException.java
diff --git a/src/main/groovy/io/kamax/mxisd/exception/JsonMemberNotFoundException.java b/src/main/java/io/kamax/mxisd/exception/JsonMemberNotFoundException.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/exception/JsonMemberNotFoundException.java
rename to src/main/java/io/kamax/mxisd/exception/JsonMemberNotFoundException.java
diff --git a/src/main/groovy/io/kamax/mxisd/exception/MappingAlreadyExistsException.java b/src/main/java/io/kamax/mxisd/exception/MappingAlreadyExistsException.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/exception/MappingAlreadyExistsException.java
rename to src/main/java/io/kamax/mxisd/exception/MappingAlreadyExistsException.java
diff --git a/src/main/groovy/io/kamax/mxisd/exception/MatrixException.java b/src/main/java/io/kamax/mxisd/exception/MatrixException.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/exception/MatrixException.java
rename to src/main/java/io/kamax/mxisd/exception/MatrixException.java
diff --git a/src/main/groovy/io/kamax/mxisd/exception/MxisdException.java b/src/main/java/io/kamax/mxisd/exception/MxisdException.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/exception/MxisdException.java
rename to src/main/java/io/kamax/mxisd/exception/MxisdException.java
diff --git a/src/main/groovy/io/kamax/mxisd/exception/NotAllowedException.java b/src/main/java/io/kamax/mxisd/exception/NotAllowedException.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/exception/NotAllowedException.java
rename to src/main/java/io/kamax/mxisd/exception/NotAllowedException.java
diff --git a/src/main/groovy/io/kamax/mxisd/exception/NotImplementedException.groovy b/src/main/java/io/kamax/mxisd/exception/NotImplementedException.java
similarity index 87%
rename from src/main/groovy/io/kamax/mxisd/exception/NotImplementedException.groovy
rename to src/main/java/io/kamax/mxisd/exception/NotImplementedException.java
index 5912136..7a3ea63 100644
--- a/src/main/groovy/io/kamax/mxisd/exception/NotImplementedException.groovy
+++ b/src/main/java/io/kamax/mxisd/exception/NotImplementedException.java
@@ -18,10 +18,10 @@
* along with this program. If not, see .
*/
-package io.kamax.mxisd.exception
+package io.kamax.mxisd.exception;
-import org.springframework.http.HttpStatus
-import org.springframework.web.bind.annotation.ResponseStatus
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(value = HttpStatus.NOT_IMPLEMENTED)
public class NotImplementedException extends RuntimeException {
diff --git a/src/main/groovy/io/kamax/mxisd/exception/ObjectNotFoundException.java b/src/main/java/io/kamax/mxisd/exception/ObjectNotFoundException.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/exception/ObjectNotFoundException.java
rename to src/main/java/io/kamax/mxisd/exception/ObjectNotFoundException.java
diff --git a/src/main/groovy/io/kamax/mxisd/exception/RemoteIdentityServerException.java b/src/main/java/io/kamax/mxisd/exception/RemoteIdentityServerException.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/exception/RemoteIdentityServerException.java
rename to src/main/java/io/kamax/mxisd/exception/RemoteIdentityServerException.java
diff --git a/src/main/groovy/io/kamax/mxisd/exception/SessionNotValidatedException.java b/src/main/java/io/kamax/mxisd/exception/SessionNotValidatedException.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/exception/SessionNotValidatedException.java
rename to src/main/java/io/kamax/mxisd/exception/SessionNotValidatedException.java
diff --git a/src/main/groovy/io/kamax/mxisd/exception/SessionUnknownException.java b/src/main/java/io/kamax/mxisd/exception/SessionUnknownException.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/exception/SessionUnknownException.java
rename to src/main/java/io/kamax/mxisd/exception/SessionUnknownException.java
diff --git a/src/main/groovy/io/kamax/mxisd/invitation/IThreePidInvite.java b/src/main/java/io/kamax/mxisd/invitation/IThreePidInvite.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/invitation/IThreePidInvite.java
rename to src/main/java/io/kamax/mxisd/invitation/IThreePidInvite.java
diff --git a/src/main/groovy/io/kamax/mxisd/invitation/IThreePidInviteReply.java b/src/main/java/io/kamax/mxisd/invitation/IThreePidInviteReply.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/invitation/IThreePidInviteReply.java
rename to src/main/java/io/kamax/mxisd/invitation/IThreePidInviteReply.java
diff --git a/src/main/groovy/io/kamax/mxisd/invitation/InvitationManager.java b/src/main/java/io/kamax/mxisd/invitation/InvitationManager.java
similarity index 91%
rename from src/main/groovy/io/kamax/mxisd/invitation/InvitationManager.java
rename to src/main/java/io/kamax/mxisd/invitation/InvitationManager.java
index 06a0d39..3f1ebc5 100644
--- a/src/main/groovy/io/kamax/mxisd/invitation/InvitationManager.java
+++ b/src/main/java/io/kamax/mxisd/invitation/InvitationManager.java
@@ -21,6 +21,8 @@
package io.kamax.mxisd.invitation;
import com.google.gson.Gson;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
import io.kamax.matrix.MatrixID;
import io.kamax.mxisd.config.DnsOverwrite;
import io.kamax.mxisd.config.DnsOverwriteEntry;
@@ -45,8 +47,6 @@ import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContextBuilder;
-import org.json.JSONArray;
-import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -153,13 +153,13 @@ public class InvitationManager {
return reply.getInvite().getSender().getId() + ":" + reply.getInvite().getRoomId() + ":" + reply.getInvite().getMedium() + ":" + reply.getInvite().getAddress();
}
- String getSrvRecordName(String domain) {
+ private String getSrvRecordName(String domain) {
return "_matrix._tcp." + domain;
}
// TODO use caching mechanism
// TODO export in matrix-java-sdk
- String findHomeserverForDomain(String domain) {
+ private String findHomeserverForDomain(String domain) {
Optional entryOpt = dns.findHost(domain);
if (entryOpt.isPresent()) {
DnsOverwriteEntry entry = entryOpt.get();
@@ -264,28 +264,28 @@ public class InvitationManager {
new Thread(() -> { // FIXME need to make this retry-able and within a general background working pool
HttpPost req = new HttpPost(hsUrlOpt + "/_matrix/federation/v1/3pid/onbind");
// Expected body: https://matrix.to/#/!HUeDbmFUsWAhxHHvFG:matrix.org/$150469846739DCLWc:matrix.trancendances.fr
- JSONObject obj = new JSONObject(); // TODO use Gson instead
- obj.put("mxid", mxid);
- obj.put("token", reply.getToken());
- obj.put("signatures", signMgr.signMessageJson(obj.toString()));
+ JsonObject obj = new JsonObject();
+ obj.addProperty("mxid", mxid);
+ obj.addProperty("token", reply.getToken());
+ obj.add("signatures", signMgr.signMessageGson(obj.toString()));
- JSONObject objUp = new JSONObject();
- objUp.put("mxid", mxid);
- objUp.put("medium", medium);
- objUp.put("address", address);
- objUp.put("sender", reply.getInvite().getSender().getId());
- objUp.put("room_id", reply.getInvite().getRoomId());
- objUp.put("signed", obj);
+ JsonObject objUp = new JsonObject();
+ objUp.addProperty("mxid", mxid);
+ objUp.addProperty("medium", medium);
+ objUp.addProperty("address", address);
+ objUp.addProperty("sender", reply.getInvite().getSender().getId());
+ objUp.addProperty("room_id", reply.getInvite().getRoomId());
+ objUp.add("signed", obj);
- JSONObject content = new JSONObject(); // TODO use Gson instead
- JSONArray invites = new JSONArray();
- invites.put(objUp);
- content.put("invites", invites);
- content.put("medium", medium);
- content.put("address", address);
- content.put("mxid", mxid);
+ JsonObject content = new JsonObject();
+ JsonArray invites = new JsonArray();
+ invites.add(objUp);
+ content.add("invites", invites);
+ content.addProperty("medium", medium);
+ content.addProperty("address", address);
+ content.addProperty("mxid", mxid);
- content.put("signatures", signMgr.signMessageJson(content.toString()));
+ content.add("signatures", signMgr.signMessageGson(content.toString()));
StringEntity entity = new StringEntity(content.toString(), StandardCharsets.UTF_8);
entity.setContentType("application/json");
@@ -313,7 +313,7 @@ public class InvitationManager {
private IThreePidInviteReply reply;
- public MappingChecker(IThreePidInviteReply reply) {
+ MappingChecker(IThreePidInviteReply reply) {
this.reply = reply;
}
diff --git a/src/main/groovy/io/kamax/mxisd/invitation/ThreePidInvite.java b/src/main/java/io/kamax/mxisd/invitation/ThreePidInvite.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/invitation/ThreePidInvite.java
rename to src/main/java/io/kamax/mxisd/invitation/ThreePidInvite.java
diff --git a/src/main/groovy/io/kamax/mxisd/invitation/ThreePidInviteReply.java b/src/main/java/io/kamax/mxisd/invitation/ThreePidInviteReply.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/invitation/ThreePidInviteReply.java
rename to src/main/java/io/kamax/mxisd/invitation/ThreePidInviteReply.java
diff --git a/src/main/java/io/kamax/mxisd/key/KeyManager.java b/src/main/java/io/kamax/mxisd/key/KeyManager.java
new file mode 100644
index 0000000..bb72a46
--- /dev/null
+++ b/src/main/java/io/kamax/mxisd/key/KeyManager.java
@@ -0,0 +1,115 @@
+/*
+ * 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 .
+ */
+
+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 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());
+ }
+
+}
diff --git a/src/main/groovy/io/kamax/mxisd/lookup/ALookupRequest.java b/src/main/java/io/kamax/mxisd/lookup/ALookupRequest.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/lookup/ALookupRequest.java
rename to src/main/java/io/kamax/mxisd/lookup/ALookupRequest.java
diff --git a/src/main/groovy/io/kamax/mxisd/lookup/BulkLookupRequest.java b/src/main/java/io/kamax/mxisd/lookup/BulkLookupRequest.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/lookup/BulkLookupRequest.java
rename to src/main/java/io/kamax/mxisd/lookup/BulkLookupRequest.java
diff --git a/src/main/groovy/io/kamax/mxisd/lookup/SingleLookupReply.java b/src/main/java/io/kamax/mxisd/lookup/SingleLookupReply.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/lookup/SingleLookupReply.java
rename to src/main/java/io/kamax/mxisd/lookup/SingleLookupReply.java
diff --git a/src/main/groovy/io/kamax/mxisd/lookup/SingleLookupRequest.groovy b/src/main/java/io/kamax/mxisd/lookup/SingleLookupRequest.java
similarity index 67%
rename from src/main/groovy/io/kamax/mxisd/lookup/SingleLookupRequest.groovy
rename to src/main/java/io/kamax/mxisd/lookup/SingleLookupRequest.java
index 3f36ae6..e0013d2 100644
--- a/src/main/groovy/io/kamax/mxisd/lookup/SingleLookupRequest.groovy
+++ b/src/main/java/io/kamax/mxisd/lookup/SingleLookupRequest.java
@@ -18,27 +18,27 @@
* along with this program. If not, see .
*/
-package io.kamax.mxisd.lookup
+package io.kamax.mxisd.lookup;
-class SingleLookupRequest extends ALookupRequest {
+public class SingleLookupRequest extends ALookupRequest {
- private String type
- private String threePid
+ private String type;
+ private String threePid;
- String getType() {
- return type
+ public String getType() {
+ return type;
}
- void setType(String type) {
- this.type = type
+ public void setType(String type) {
+ this.type = type;
}
- String getThreePid() {
- return threePid
+ public String getThreePid() {
+ return threePid;
}
- void setThreePid(String threePid) {
- this.threePid = threePid
+ public void setThreePid(String threePid) {
+ this.threePid = threePid;
}
}
diff --git a/src/main/groovy/io/kamax/mxisd/lookup/ThreePidMapping.java b/src/main/java/io/kamax/mxisd/lookup/ThreePidMapping.java
similarity index 95%
rename from src/main/groovy/io/kamax/mxisd/lookup/ThreePidMapping.java
rename to src/main/java/io/kamax/mxisd/lookup/ThreePidMapping.java
index 5777ad7..26b15ec 100644
--- a/src/main/groovy/io/kamax/mxisd/lookup/ThreePidMapping.java
+++ b/src/main/java/io/kamax/mxisd/lookup/ThreePidMapping.java
@@ -20,11 +20,13 @@
package io.kamax.mxisd.lookup;
-import groovy.json.JsonOutput;
+import com.google.gson.Gson;
import io.kamax.mxisd.ThreePid;
public class ThreePidMapping {
+ private static Gson gson = new Gson();
+
private String medium;
private String value;
private String mxid;
@@ -87,7 +89,7 @@ public class ThreePidMapping {
@Override
public String toString() {
- return JsonOutput.toJson(this);
+ return gson.toJson(this);
}
}
diff --git a/src/main/groovy/io/kamax/mxisd/lookup/ThreePidValidation.java b/src/main/java/io/kamax/mxisd/lookup/ThreePidValidation.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/lookup/ThreePidValidation.java
rename to src/main/java/io/kamax/mxisd/lookup/ThreePidValidation.java
diff --git a/src/main/groovy/io/kamax/mxisd/lookup/fetcher/IBridgeFetcher.groovy b/src/main/java/io/kamax/mxisd/lookup/fetcher/IBridgeFetcher.java
similarity index 76%
rename from src/main/groovy/io/kamax/mxisd/lookup/fetcher/IBridgeFetcher.groovy
rename to src/main/java/io/kamax/mxisd/lookup/fetcher/IBridgeFetcher.java
index bd445bb..bace0fe 100644
--- a/src/main/groovy/io/kamax/mxisd/lookup/fetcher/IBridgeFetcher.groovy
+++ b/src/main/java/io/kamax/mxisd/lookup/fetcher/IBridgeFetcher.java
@@ -18,16 +18,19 @@
* along with this program. If not, see .
*/
-package io.kamax.mxisd.lookup.fetcher
+package io.kamax.mxisd.lookup.fetcher;
-import io.kamax.mxisd.lookup.SingleLookupReply
-import io.kamax.mxisd.lookup.SingleLookupRequest
-import io.kamax.mxisd.lookup.ThreePidMapping
+import io.kamax.mxisd.lookup.SingleLookupReply;
+import io.kamax.mxisd.lookup.SingleLookupRequest;
+import io.kamax.mxisd.lookup.ThreePidMapping;
-interface IBridgeFetcher {
+import java.util.List;
+import java.util.Optional;
- Optional find(SingleLookupRequest request)
+public interface IBridgeFetcher {
- List populate(List mappings)
+ Optional find(SingleLookupRequest request);
+
+ List populate(List mappings);
}
diff --git a/src/main/groovy/io/kamax/mxisd/lookup/fetcher/IRemoteIdentityServerFetcher.groovy b/src/main/java/io/kamax/mxisd/lookup/fetcher/IRemoteIdentityServerFetcher.java
similarity index 72%
rename from src/main/groovy/io/kamax/mxisd/lookup/fetcher/IRemoteIdentityServerFetcher.groovy
rename to src/main/java/io/kamax/mxisd/lookup/fetcher/IRemoteIdentityServerFetcher.java
index aeb81d5..bfd62e8 100644
--- a/src/main/groovy/io/kamax/mxisd/lookup/fetcher/IRemoteIdentityServerFetcher.groovy
+++ b/src/main/java/io/kamax/mxisd/lookup/fetcher/IRemoteIdentityServerFetcher.java
@@ -18,18 +18,21 @@
* along with this program. If not, see .
*/
-package io.kamax.mxisd.lookup.fetcher
+package io.kamax.mxisd.lookup.fetcher;
-import io.kamax.mxisd.lookup.SingleLookupReply
-import io.kamax.mxisd.lookup.SingleLookupRequest
-import io.kamax.mxisd.lookup.ThreePidMapping
+import io.kamax.mxisd.lookup.SingleLookupReply;
+import io.kamax.mxisd.lookup.SingleLookupRequest;
+import io.kamax.mxisd.lookup.ThreePidMapping;
-interface IRemoteIdentityServerFetcher {
+import java.util.List;
+import java.util.Optional;
- boolean isUsable(String remote)
+public interface IRemoteIdentityServerFetcher {
- Optional find(String remote, SingleLookupRequest request)
+ boolean isUsable(String remote);
- List find(String remote, List mappings)
+ Optional find(String remote, SingleLookupRequest request);
+
+ List find(String remote, List mappings);
}
diff --git a/src/main/groovy/io/kamax/mxisd/lookup/provider/BridgeFetcher.java b/src/main/java/io/kamax/mxisd/lookup/provider/BridgeFetcher.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/lookup/provider/BridgeFetcher.java
rename to src/main/java/io/kamax/mxisd/lookup/provider/BridgeFetcher.java
diff --git a/src/main/groovy/io/kamax/mxisd/lookup/provider/DnsLookupProvider.groovy b/src/main/java/io/kamax/mxisd/lookup/provider/DnsLookupProvider.java
similarity index 57%
rename from src/main/groovy/io/kamax/mxisd/lookup/provider/DnsLookupProvider.groovy
rename to src/main/java/io/kamax/mxisd/lookup/provider/DnsLookupProvider.java
index 28ef1a8..89e1f77 100644
--- a/src/main/groovy/io/kamax/mxisd/lookup/provider/DnsLookupProvider.groovy
+++ b/src/main/java/io/kamax/mxisd/lookup/provider/DnsLookupProvider.java
@@ -18,170 +18,163 @@
* along with this program. If not, see .
*/
-package io.kamax.mxisd.lookup.provider
+package io.kamax.mxisd.lookup.provider;
-import io.kamax.mxisd.config.MatrixConfig
-import io.kamax.mxisd.lookup.SingleLookupReply
-import io.kamax.mxisd.lookup.SingleLookupRequest
-import io.kamax.mxisd.lookup.ThreePidMapping
-import io.kamax.mxisd.lookup.fetcher.IRemoteIdentityServerFetcher
-import io.kamax.mxisd.matrix.IdentityServerUtils
-import org.apache.commons.lang.StringUtils
-import org.slf4j.Logger
-import org.slf4j.LoggerFactory
-import org.springframework.beans.factory.annotation.Autowired
-import org.springframework.stereotype.Component
+import io.kamax.mxisd.config.MatrixConfig;
+import io.kamax.mxisd.lookup.SingleLookupReply;
+import io.kamax.mxisd.lookup.SingleLookupRequest;
+import io.kamax.mxisd.lookup.ThreePidMapping;
+import io.kamax.mxisd.lookup.fetcher.IRemoteIdentityServerFetcher;
+import io.kamax.mxisd.matrix.IdentityServerUtils;
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
-import java.util.concurrent.ForkJoinPool
-import java.util.concurrent.RecursiveTask
-import java.util.function.Function
+import java.util.*;
+import java.util.concurrent.ForkJoinPool;
+import java.util.concurrent.RecursiveTask;
@Component
class DnsLookupProvider implements IThreePidProvider {
- private Logger log = LoggerFactory.getLogger(DnsLookupProvider.class)
+ private Logger log = LoggerFactory.getLogger(DnsLookupProvider.class);
@Autowired
- private MatrixConfig mxCfg
+ private MatrixConfig mxCfg;
@Autowired
- private IRemoteIdentityServerFetcher fetcher
+ private IRemoteIdentityServerFetcher fetcher;
@Override
- boolean isEnabled() {
- return true
+ public boolean isEnabled() {
+ return true;
}
@Override
- boolean isLocal() {
- return false
+ public boolean isLocal() {
+ return false;
}
@Override
- int getPriority() {
- return 10
+ public int getPriority() {
+ return 10;
}
- Optional getDomain(String email) {
- int atIndex = email.lastIndexOf("@")
+ private Optional getDomain(String email) {
+ int atIndex = email.lastIndexOf("@");
if (atIndex == -1) {
- return Optional.empty()
+ return Optional.empty();
}
- return Optional.of(email.substring(atIndex + 1))
+ return Optional.of(email.substring(atIndex + 1));
}
// TODO use caching mechanism
- Optional findIdentityServerForDomain(String domain) {
+ private Optional findIdentityServerForDomain(String domain) {
if (StringUtils.equals(mxCfg.getDomain(), domain)) {
- log.info("We are authoritative for {}, no remote lookup", domain)
- return Optional.empty()
+ log.info("We are authoritative for {}, no remote lookup", domain);
+ return Optional.empty();
}
- return IdentityServerUtils.findIsUrlForDomain(domain)
+ return IdentityServerUtils.findIsUrlForDomain(domain);
}
@Override
- Optional find(SingleLookupRequest request) {
+ public Optional find(SingleLookupRequest request) {
if (!StringUtils.equals("email", request.getType())) { // TODO use enum
- log.info("Skipping unsupported type {} for {}", request.getType(), request.getThreePid())
- return Optional.empty()
+ log.info("Skipping unsupported type {} for {}", request.getType(), request.getThreePid());
+ return Optional.empty();
}
- log.info("Performing DNS lookup for {}", request.getThreePid())
+ log.info("Performing DNS lookup for {}", request.getThreePid());
- String domain = request.getThreePid().substring(request.getThreePid().lastIndexOf("@") + 1)
- log.info("Domain name for {}: {}", request.getThreePid(), domain)
- Optional baseUrl = findIdentityServerForDomain(domain)
+ String domain = request.getThreePid().substring(request.getThreePid().lastIndexOf("@") + 1);
+ log.info("Domain name for {}: {}", request.getThreePid(), domain);
+ Optional baseUrl = findIdentityServerForDomain(domain);
if (baseUrl.isPresent()) {
- return fetcher.find(baseUrl.get(), request)
+ return fetcher.find(baseUrl.get(), request);
}
- return Optional.empty()
+ return Optional.empty();
}
@Override
- List populate(List mappings) {
- Map> domains = new HashMap<>()
+ public List populate(List mappings) {
+ Map> domains = new HashMap<>();
for (ThreePidMapping mapping : mappings) {
if (!StringUtils.equals("email", mapping.getMedium())) {
- log.info("Skipping unsupported type {} for {}", mapping.getMedium(), mapping.getValue())
- continue
+ log.info("Skipping unsupported type {} for {}", mapping.getMedium(), mapping.getValue());
+ continue;
}
- Optional domainOpt = getDomain(mapping.getValue())
+ Optional domainOpt = getDomain(mapping.getValue());
if (!domainOpt.isPresent()) {
- log.warn("No domain for 3PID {}", mapping.getValue())
- continue
+ log.warn("No domain for 3PID {}", mapping.getValue());
+ continue;
}
- String domain = domainOpt.get()
- List domainMappings = domains.computeIfAbsent(domain, new Function>() {
-
- @Override
- List apply(String s) {
- return new ArrayList<>()
- }
-
- })
- domainMappings.add(mapping)
+ String domain = domainOpt.get();
+ List domainMappings = domains.computeIfAbsent(domain, s -> new ArrayList<>());
+ domainMappings.add(mapping);
}
- log.info("Looking mappings across {} domains", domains.keySet().size())
- ForkJoinPool pool = new ForkJoinPool()
+ log.info("Looking mappings across {} domains", domains.keySet().size());
+ ForkJoinPool pool = ForkJoinPool.commonPool();
RecursiveTask> task = new RecursiveTask>() {
@Override
protected List compute() {
- List mappingsFound = new ArrayList<>()
- List tasks = new ArrayList<>()
+ List mappingsFound = new ArrayList<>();
+ List tasks = new ArrayList<>();
for (String domain : domains.keySet()) {
- DomainBulkLookupTask domainTask = new DomainBulkLookupTask(domain, domains.get(domain))
- domainTask.fork()
- tasks.add(domainTask)
+ DomainBulkLookupTask domainTask = new DomainBulkLookupTask(domain, domains.get(domain));
+ domainTask.fork();
+ tasks.add(domainTask);
}
for (DomainBulkLookupTask task : tasks) {
- mappingsFound.addAll(task.join())
+ mappingsFound.addAll(task.join());
}
- return mappingsFound
+ return mappingsFound;
}
- }
- pool.submit(task)
- pool.shutdown()
+ };
+ pool.submit(task);
+ pool.shutdown();
- List mappingsFound = task.join()
- log.info("Found {} mappings overall", mappingsFound.size())
- return mappingsFound
+ List mappingsFound = task.join();
+ log.info("Found {} mappings overall", mappingsFound.size());
+ return mappingsFound;
}
private class DomainBulkLookupTask extends RecursiveTask> {
- private String domain
- private List mappings
+ private String domain;
+ private List mappings;
DomainBulkLookupTask(String domain, List mappings) {
- this.domain = domain
- this.mappings = mappings
+ this.domain = domain;
+ this.mappings = mappings;
}
@Override
protected List compute() {
- List domainMappings = new ArrayList<>()
+ List domainMappings = new ArrayList<>();
- Optional baseUrl = findIdentityServerForDomain(domain)
+ Optional baseUrl = findIdentityServerForDomain(domain);
if (!baseUrl.isPresent()) {
- log.info("No usable Identity server for domain {}", domain)
+ log.info("No usable Identity server for domain {}", domain);
} else {
- domainMappings.addAll(fetcher.find(baseUrl.get(), mappings))
- log.info("Found {} mappings in domain {}", domainMappings.size(), domain)
+ domainMappings.addAll(fetcher.find(baseUrl.get(), mappings));
+ log.info("Found {} mappings in domain {}", domainMappings.size(), domain);
}
- return domainMappings
+ return domainMappings;
}
}
diff --git a/src/main/groovy/io/kamax/mxisd/lookup/provider/ForwarderProvider.groovy b/src/main/java/io/kamax/mxisd/lookup/provider/ForwarderProvider.java
similarity index 57%
rename from src/main/groovy/io/kamax/mxisd/lookup/provider/ForwarderProvider.groovy
rename to src/main/java/io/kamax/mxisd/lookup/provider/ForwarderProvider.java
index 1ef903a..badaa1a 100644
--- a/src/main/groovy/io/kamax/mxisd/lookup/provider/ForwarderProvider.groovy
+++ b/src/main/java/io/kamax/mxisd/lookup/provider/ForwarderProvider.java
@@ -18,71 +18,75 @@
* along with this program. If not, see .
*/
-package io.kamax.mxisd.lookup.provider
+package io.kamax.mxisd.lookup.provider;
-import io.kamax.mxisd.config.ForwardConfig
-import io.kamax.mxisd.lookup.SingleLookupReply
-import io.kamax.mxisd.lookup.SingleLookupRequest
-import io.kamax.mxisd.lookup.ThreePidMapping
-import io.kamax.mxisd.lookup.fetcher.IRemoteIdentityServerFetcher
-import org.slf4j.Logger
-import org.slf4j.LoggerFactory
-import org.springframework.beans.factory.annotation.Autowired
-import org.springframework.stereotype.Component
+import io.kamax.mxisd.config.ForwardConfig;
+import io.kamax.mxisd.lookup.SingleLookupReply;
+import io.kamax.mxisd.lookup.SingleLookupRequest;
+import io.kamax.mxisd.lookup.ThreePidMapping;
+import io.kamax.mxisd.lookup.fetcher.IRemoteIdentityServerFetcher;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
@Component
class ForwarderProvider implements IThreePidProvider {
- private Logger log = LoggerFactory.getLogger(ForwarderProvider.class)
+ private Logger log = LoggerFactory.getLogger(ForwarderProvider.class);
@Autowired
- private ForwardConfig cfg
+ private ForwardConfig cfg;
@Autowired
- private IRemoteIdentityServerFetcher fetcher
+ private IRemoteIdentityServerFetcher fetcher;
@Override
- boolean isEnabled() {
- return true
+ public boolean isEnabled() {
+ return true;
}
@Override
- boolean isLocal() {
- return false
+ public boolean isLocal() {
+ return false;
}
@Override
- int getPriority() {
- return 0
+ public int getPriority() {
+ return 0;
}
@Override
- Optional find(SingleLookupRequest request) {
+ public Optional find(SingleLookupRequest request) {
for (String root : cfg.getServers()) {
- Optional answer = fetcher.find(root, request)
+ Optional answer = fetcher.find(root, request);
if (answer.isPresent()) {
- return answer
+ return answer;
}
}
- return Optional.empty()
+ return Optional.empty();
}
@Override
- List populate(List mappings) {
- List mappingsToDo = new ArrayList<>(mappings)
- List mappingsFoundGlobal = new ArrayList<>()
+ public List populate(List mappings) {
+ List mappingsToDo = new ArrayList<>(mappings);
+ List mappingsFoundGlobal = new ArrayList<>();
for (String root : cfg.getServers()) {
- log.info("{} mappings remaining: {}", mappingsToDo.size(), mappingsToDo)
- log.info("Querying {}", root)
- List mappingsFound = fetcher.find(root, mappingsToDo)
- log.info("{} returned {} mappings", root, mappingsFound.size())
- mappingsFoundGlobal.addAll(mappingsFound)
- mappingsToDo.removeAll(mappingsFound)
+ log.info("{} mappings remaining: {}", mappingsToDo.size(), mappingsToDo);
+ log.info("Querying {}", root);
+ List mappingsFound = fetcher.find(root, mappingsToDo);
+ log.info("{} returned {} mappings", root, mappingsFound.size());
+ mappingsFoundGlobal.addAll(mappingsFound);
+ mappingsToDo.removeAll(mappingsFound);
}
- return mappingsFoundGlobal
+ return mappingsFoundGlobal;
}
}
diff --git a/src/main/groovy/io/kamax/mxisd/lookup/provider/IThreePidProvider.groovy b/src/main/java/io/kamax/mxisd/lookup/provider/IThreePidProvider.java
similarity index 70%
rename from src/main/groovy/io/kamax/mxisd/lookup/provider/IThreePidProvider.groovy
rename to src/main/java/io/kamax/mxisd/lookup/provider/IThreePidProvider.java
index 614cc04..204a813 100644
--- a/src/main/groovy/io/kamax/mxisd/lookup/provider/IThreePidProvider.groovy
+++ b/src/main/java/io/kamax/mxisd/lookup/provider/IThreePidProvider.java
@@ -18,25 +18,28 @@
* along with this program. If not, see .
*/
-package io.kamax.mxisd.lookup.provider
+package io.kamax.mxisd.lookup.provider;
-import io.kamax.mxisd.lookup.SingleLookupReply
-import io.kamax.mxisd.lookup.SingleLookupRequest
-import io.kamax.mxisd.lookup.ThreePidMapping
+import io.kamax.mxisd.lookup.SingleLookupReply;
+import io.kamax.mxisd.lookup.SingleLookupRequest;
+import io.kamax.mxisd.lookup.ThreePidMapping;
-interface IThreePidProvider {
+import java.util.List;
+import java.util.Optional;
- boolean isEnabled()
+public interface IThreePidProvider {
- boolean isLocal()
+ boolean isEnabled();
+
+ boolean isLocal();
/**
* Higher has more priority
*/
- int getPriority() // Should not be here but let's KISS for now
+ int getPriority(); // Should not be here but let's KISS for now
- Optional find(SingleLookupRequest request)
+ Optional find(SingleLookupRequest request);
- List populate(List mappings)
+ List populate(List mappings);
}
diff --git a/src/main/java/io/kamax/mxisd/lookup/provider/RemoteIdentityServerFetcher.java b/src/main/java/io/kamax/mxisd/lookup/provider/RemoteIdentityServerFetcher.java
new file mode 100644
index 0000000..bec8bd3
--- /dev/null
+++ b/src/main/java/io/kamax/mxisd/lookup/provider/RemoteIdentityServerFetcher.java
@@ -0,0 +1,128 @@
+/*
+ * 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 .
+ */
+
+package io.kamax.mxisd.lookup.provider;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParseException;
+import io.kamax.mxisd.controller.v1.ClientBulkLookupRequest;
+import io.kamax.mxisd.exception.InvalidResponseJsonException;
+import io.kamax.mxisd.lookup.SingleLookupReply;
+import io.kamax.mxisd.lookup.SingleLookupRequest;
+import io.kamax.mxisd.lookup.ThreePidMapping;
+import io.kamax.mxisd.lookup.fetcher.IRemoteIdentityServerFetcher;
+import io.kamax.mxisd.matrix.IdentityServerUtils;
+import io.kamax.mxisd.util.GsonParser;
+import io.kamax.mxisd.util.RestClientUtils;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.context.annotation.Scope;
+import org.springframework.stereotype.Component;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+@Component
+@Scope("prototype")
+@Lazy
+public class RemoteIdentityServerFetcher implements IRemoteIdentityServerFetcher {
+
+ private Logger log = LoggerFactory.getLogger(RemoteIdentityServerFetcher.class);
+
+ private Gson gson = new Gson();
+ private GsonParser parser = new GsonParser(gson);
+
+ @Override
+ public boolean isUsable(String remote) {
+ return IdentityServerUtils.isUsable(remote);
+ }
+
+ @Override
+ public Optional find(String remote, SingleLookupRequest request) {
+ log.info("Looking up {} 3PID {} using {}", request.getType(), request.getThreePid(), remote);
+
+ try {
+ HttpURLConnection rootSrvConn = (HttpURLConnection) new URL(
+ remote + "/_matrix/identity/api/v1/lookup?medium=" + request.getType() + "&address=" + request.getThreePid()
+ ).openConnection();
+ JsonObject obj = parser.parse(rootSrvConn.getInputStream());
+ if (obj.has("address")) {
+ log.info("Found 3PID mapping: {}", gson.toJson(obj));
+
+ return Optional.of(SingleLookupReply.fromRecursive(request, gson.toJson(obj)));
+ }
+
+ log.info("Empty 3PID mapping from {}", remote);
+ return Optional.empty();
+ } catch (IOException e) {
+ log.warn("Error looking up 3PID mapping {}: {}", request.getThreePid(), e.getMessage());
+ return Optional.empty();
+ } catch (JsonParseException e) {
+ log.warn("Invalid JSON answer from {}", remote);
+ return Optional.empty();
+ }
+ }
+
+ @Override
+ public List find(String remote, List mappings) {
+ List mappingsFound = new ArrayList<>();
+
+ ClientBulkLookupRequest mappingRequest = new ClientBulkLookupRequest();
+ mappingRequest.setMappings(mappings);
+
+ String url = remote + "/_matrix/identity/api/v1/bulk_lookup";
+ CloseableHttpClient client = HttpClients.createDefault();
+ try {
+ HttpPost request = RestClientUtils.post(url, mappingRequest);
+ try (CloseableHttpResponse response = client.execute(request)) {
+ if (response.getStatusLine().getStatusCode() != 200) {
+ log.info("Could not perform lookup at {} due to HTTP return code: {}", url, response.getStatusLine().getStatusCode());
+ return mappingsFound;
+ }
+
+ ClientBulkLookupRequest input = parser.parse(response, ClientBulkLookupRequest.class);
+ for (List mappingRaw : input.getThreepids()) {
+ ThreePidMapping mapping = new ThreePidMapping();
+ mapping.setMedium(mappingRaw.get(0));
+ mapping.setValue(mappingRaw.get(1));
+ mapping.setMxid(mappingRaw.get(2));
+ mappingsFound.add(mapping);
+ }
+ }
+ } catch (IOException e) {
+ log.warn("Unable to fetch remote lookup data: {}", e.getMessage());
+ } catch (InvalidResponseJsonException e) {
+ log.info("HTTP response from {} was empty/invalid", remote);
+ }
+
+ return mappingsFound;
+ }
+
+}
diff --git a/src/main/groovy/io/kamax/mxisd/lookup/strategy/LookupStrategy.groovy b/src/main/java/io/kamax/mxisd/lookup/strategy/LookupStrategy.java
similarity index 68%
rename from src/main/groovy/io/kamax/mxisd/lookup/strategy/LookupStrategy.groovy
rename to src/main/java/io/kamax/mxisd/lookup/strategy/LookupStrategy.java
index 44b67f0..66d3d84 100644
--- a/src/main/groovy/io/kamax/mxisd/lookup/strategy/LookupStrategy.groovy
+++ b/src/main/java/io/kamax/mxisd/lookup/strategy/LookupStrategy.java
@@ -18,28 +18,31 @@
* along with this program. If not, see .
*/
-package io.kamax.mxisd.lookup.strategy
+package io.kamax.mxisd.lookup.strategy;
-import io.kamax.mxisd.lookup.BulkLookupRequest
-import io.kamax.mxisd.lookup.SingleLookupReply
-import io.kamax.mxisd.lookup.SingleLookupRequest
-import io.kamax.mxisd.lookup.ThreePidMapping
-import io.kamax.mxisd.lookup.provider.IThreePidProvider
+import io.kamax.mxisd.lookup.BulkLookupRequest;
+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;
-interface LookupStrategy {
+import java.util.List;
+import java.util.Optional;
- List getLocalProviders()
+public interface LookupStrategy {
- Optional find(String medium, String address, boolean recursive)
+ List getLocalProviders();
+
+ Optional find(String medium, String address, boolean recursive);
Optional findLocal(String medium, String address);
Optional findRemote(String medium, String address);
- Optional find(SingleLookupRequest request)
+ Optional find(SingleLookupRequest request);
- Optional findRecursive(SingleLookupRequest request)
+ Optional findRecursive(SingleLookupRequest request);
- List find(BulkLookupRequest requests)
+ List find(BulkLookupRequest requests);
}
diff --git a/src/main/java/io/kamax/mxisd/lookup/strategy/RecursivePriorityLookupStrategy.java b/src/main/java/io/kamax/mxisd/lookup/strategy/RecursivePriorityLookupStrategy.java
new file mode 100644
index 0000000..7b17378
--- /dev/null
+++ b/src/main/java/io/kamax/mxisd/lookup/strategy/RecursivePriorityLookupStrategy.java
@@ -0,0 +1,206 @@
+/*
+ * 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 .
+ */
+
+package io.kamax.mxisd.lookup.strategy;
+
+import edazdarevic.commons.net.CIDRUtils;
+import io.kamax.mxisd.config.RecursiveLookupConfig;
+import io.kamax.mxisd.exception.ConfigurationException;
+import io.kamax.mxisd.lookup.*;
+import io.kamax.mxisd.lookup.fetcher.IBridgeFetcher;
+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 javax.annotation.PostConstruct;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+@Component
+public class RecursivePriorityLookupStrategy implements LookupStrategy {
+
+ private Logger log = LoggerFactory.getLogger(RecursivePriorityLookupStrategy.class);
+
+ @Autowired
+ private RecursiveLookupConfig recursiveCfg;
+
+ @Autowired
+ private List providers;
+
+ @Autowired
+ private IBridgeFetcher bridge;
+
+ private List allowedCidr = new ArrayList<>();
+
+ @PostConstruct
+ private void build() throws UnknownHostException {
+ try {
+ log.info("Found {} providers", providers.size());
+
+ providers.sort((o1, o2) -> Integer.compare(o2.getPriority(), o1.getPriority()));
+
+ log.info("Recursive lookup enabled: {}", recursiveCfg.isEnabled());
+ for (String cidr : recursiveCfg.getAllowedCidr()) {
+ log.info("{} is allowed for recursion", cidr);
+ allowedCidr.add(new CIDRUtils(cidr));
+ }
+ } catch (UnknownHostException e) {
+ throw new ConfigurationException("lookup.recursive.allowedCidrs", "Allowed CIDRs");
+ }
+ }
+
+ private boolean isAllowedForRecursive(String source) {
+ boolean canRecurse = false;
+
+ try {
+ if (recursiveCfg.isEnabled()) {
+ log.debug("Checking {} CIDRs for recursion", allowedCidr.size());
+ for (CIDRUtils cidr : allowedCidr) {
+ if (cidr.isInRange(source)) {
+ log.debug("{} is in range {}, allowing recursion", source, cidr.getNetworkAddress());
+ canRecurse = true;
+ break;
+ } else {
+ log.debug("{} is not in range {}", source, cidr.getNetworkAddress());
+ }
+ }
+ }
+ } catch (UnknownHostException e) {
+ // this should never happened as we should have only IP ranges!
+ log.error("Unexpected {} exception: {}", e.getClass().getSimpleName(), e.getMessage());
+ }
+
+ return canRecurse;
+ }
+
+ private List listUsableProviders(ALookupRequest request) {
+ return listUsableProviders(request, false);
+ }
+
+ private List listUsableProviders(ALookupRequest request, boolean forceRecursive) {
+ List usableProviders = new ArrayList<>();
+
+ boolean canRecurse = forceRecursive || isAllowedForRecursive(request.getRequester());
+
+ log.info("Host {} allowed for recursion: {}", request.getRequester(), canRecurse);
+ for (IThreePidProvider provider : providers) {
+ if (provider.isEnabled() && (provider.isLocal() || canRecurse || forceRecursive)) {
+ usableProviders.add(provider);
+ }
+ }
+
+ return usableProviders;
+ }
+
+ @Override
+ public List getLocalProviders() {
+ return providers.stream().filter(iThreePidProvider -> iThreePidProvider.isEnabled() && iThreePidProvider.isLocal()).collect(Collectors.toList());
+ }
+
+ public List getRemoteProviders() {
+ return providers.stream().filter(iThreePidProvider -> iThreePidProvider.isEnabled() && !iThreePidProvider.isLocal()).collect(Collectors.toList());
+ }
+
+ private static SingleLookupRequest build(String medium, String address) {
+ SingleLookupRequest req = new SingleLookupRequest();
+ req.setType(medium);
+ req.setThreePid(address);
+ req.setRequester("Internal");
+ return req;
+ }
+
+ @Override
+ public Optional find(String medium, String address, boolean recursive) {
+ return find(build(medium, address), recursive);
+ }
+
+ @Override
+ public Optional findLocal(String medium, String address) {
+ return find(build(medium, address), getLocalProviders());
+ }
+
+ @Override
+ public Optional findRemote(String medium, String address) {
+ return find(build(medium, address), getRemoteProviders());
+ }
+
+ public Optional find(SingleLookupRequest request, boolean forceRecursive) {
+ return find(request, listUsableProviders(request, forceRecursive));
+ }
+
+ public Optional find(SingleLookupRequest request, List providers) {
+ for (IThreePidProvider provider : providers) {
+ Optional lookupDataOpt = provider.find(request);
+ if (lookupDataOpt.isPresent()) {
+ return lookupDataOpt;
+ }
+ }
+
+ if (
+ recursiveCfg.getBridge() != null &&
+ recursiveCfg.getBridge().getEnabled() &&
+ (!recursiveCfg.getBridge().getRecursiveOnly() || isAllowedForRecursive(request.getRequester()))
+ ) {
+ log.info("Using bridge failover for lookup");
+ return bridge.find(request);
+ }
+
+ return Optional.empty();
+ }
+
+ @Override
+ public Optional find(SingleLookupRequest request) {
+ return find(request, false);
+ }
+
+ @Override
+ public Optional findRecursive(SingleLookupRequest request) {
+ return find(request, true);
+ }
+
+ @Override
+ public List find(BulkLookupRequest request) {
+ List mapToDo = new ArrayList<>(request.getMappings());
+ List mapFoundAll = new ArrayList<>();
+
+ for (IThreePidProvider provider : listUsableProviders(request)) {
+ if (mapToDo.isEmpty()) {
+ log.info("No more mappings to lookup");
+ break;
+ } else {
+ log.info("{} mappings remaining overall", mapToDo.size());
+ }
+
+ log.info("Using provider {} for remaining mappings", provider.getClass().getSimpleName());
+ List mapFound = provider.populate(mapToDo);
+ log.info("Provider {} returned {} mappings", provider.getClass().getSimpleName(), mapFound.size());
+ mapFoundAll.addAll(mapFound);
+ mapToDo.removeAll(mapFound);
+ }
+
+ return mapFoundAll;
+ }
+
+}
diff --git a/src/main/groovy/io/kamax/mxisd/matrix/IdentityServerUtils.java b/src/main/java/io/kamax/mxisd/matrix/IdentityServerUtils.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/matrix/IdentityServerUtils.java
rename to src/main/java/io/kamax/mxisd/matrix/IdentityServerUtils.java
diff --git a/src/main/groovy/io/kamax/mxisd/notification/INotificationHandler.java b/src/main/java/io/kamax/mxisd/notification/INotificationHandler.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/notification/INotificationHandler.java
rename to src/main/java/io/kamax/mxisd/notification/INotificationHandler.java
diff --git a/src/main/groovy/io/kamax/mxisd/notification/NotificationManager.java b/src/main/java/io/kamax/mxisd/notification/NotificationManager.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/notification/NotificationManager.java
rename to src/main/java/io/kamax/mxisd/notification/NotificationManager.java
diff --git a/src/main/groovy/io/kamax/mxisd/session/SessionMananger.java b/src/main/java/io/kamax/mxisd/session/SessionMananger.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/session/SessionMananger.java
rename to src/main/java/io/kamax/mxisd/session/SessionMananger.java
diff --git a/src/main/groovy/io/kamax/mxisd/session/ValidationResult.java b/src/main/java/io/kamax/mxisd/session/ValidationResult.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/session/ValidationResult.java
rename to src/main/java/io/kamax/mxisd/session/ValidationResult.java
diff --git a/src/main/java/io/kamax/mxisd/signature/SignatureManager.java b/src/main/java/io/kamax/mxisd/signature/SignatureManager.java
new file mode 100644
index 0000000..6c45803
--- /dev/null
+++ b/src/main/java/io/kamax/mxisd/signature/SignatureManager.java
@@ -0,0 +1,79 @@
+/*
+ * 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 .
+ */
+
+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);
+ }
+ }
+
+}
diff --git a/src/main/groovy/io/kamax/mxisd/spring/ConfigurationFailureAnalyzer.java b/src/main/java/io/kamax/mxisd/spring/ConfigurationFailureAnalyzer.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/spring/ConfigurationFailureAnalyzer.java
rename to src/main/java/io/kamax/mxisd/spring/ConfigurationFailureAnalyzer.java
diff --git a/src/main/groovy/io/kamax/mxisd/storage/IStorage.java b/src/main/java/io/kamax/mxisd/storage/IStorage.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/storage/IStorage.java
rename to src/main/java/io/kamax/mxisd/storage/IStorage.java
diff --git a/src/main/groovy/io/kamax/mxisd/storage/dao/IThreePidSessionDao.java b/src/main/java/io/kamax/mxisd/storage/dao/IThreePidSessionDao.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/storage/dao/IThreePidSessionDao.java
rename to src/main/java/io/kamax/mxisd/storage/dao/IThreePidSessionDao.java
diff --git a/src/main/groovy/io/kamax/mxisd/storage/ormlite/OrmLiteSqliteStorage.java b/src/main/java/io/kamax/mxisd/storage/ormlite/OrmLiteSqliteStorage.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/storage/ormlite/OrmLiteSqliteStorage.java
rename to src/main/java/io/kamax/mxisd/storage/ormlite/OrmLiteSqliteStorage.java
diff --git a/src/main/groovy/io/kamax/mxisd/storage/ormlite/OrmLiteSqliteStorageBeanFactory.java b/src/main/java/io/kamax/mxisd/storage/ormlite/OrmLiteSqliteStorageBeanFactory.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/storage/ormlite/OrmLiteSqliteStorageBeanFactory.java
rename to src/main/java/io/kamax/mxisd/storage/ormlite/OrmLiteSqliteStorageBeanFactory.java
diff --git a/src/main/groovy/io/kamax/mxisd/storage/ormlite/ThreePidInviteIO.java b/src/main/java/io/kamax/mxisd/storage/ormlite/ThreePidInviteIO.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/storage/ormlite/ThreePidInviteIO.java
rename to src/main/java/io/kamax/mxisd/storage/ormlite/ThreePidInviteIO.java
diff --git a/src/main/groovy/io/kamax/mxisd/storage/ormlite/dao/ThreePidSessionDao.java b/src/main/java/io/kamax/mxisd/storage/ormlite/dao/ThreePidSessionDao.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/storage/ormlite/dao/ThreePidSessionDao.java
rename to src/main/java/io/kamax/mxisd/storage/ormlite/dao/ThreePidSessionDao.java
diff --git a/src/main/groovy/io/kamax/mxisd/threepid/connector/IThreePidConnector.java b/src/main/java/io/kamax/mxisd/threepid/connector/IThreePidConnector.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/threepid/connector/IThreePidConnector.java
rename to src/main/java/io/kamax/mxisd/threepid/connector/IThreePidConnector.java
diff --git a/src/main/groovy/io/kamax/mxisd/threepid/connector/email/EmailSmtpConnector.java b/src/main/java/io/kamax/mxisd/threepid/connector/email/EmailSmtpConnector.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/threepid/connector/email/EmailSmtpConnector.java
rename to src/main/java/io/kamax/mxisd/threepid/connector/email/EmailSmtpConnector.java
diff --git a/src/main/groovy/io/kamax/mxisd/threepid/connector/email/IEmailConnector.java b/src/main/java/io/kamax/mxisd/threepid/connector/email/IEmailConnector.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/threepid/connector/email/IEmailConnector.java
rename to src/main/java/io/kamax/mxisd/threepid/connector/email/IEmailConnector.java
diff --git a/src/main/groovy/io/kamax/mxisd/threepid/notification/INotificationGenerator.java b/src/main/java/io/kamax/mxisd/threepid/notification/INotificationGenerator.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/threepid/notification/INotificationGenerator.java
rename to src/main/java/io/kamax/mxisd/threepid/notification/INotificationGenerator.java
diff --git a/src/main/groovy/io/kamax/mxisd/threepid/notification/email/EmailNotificationGenerator.java b/src/main/java/io/kamax/mxisd/threepid/notification/email/EmailNotificationGenerator.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/threepid/notification/email/EmailNotificationGenerator.java
rename to src/main/java/io/kamax/mxisd/threepid/notification/email/EmailNotificationGenerator.java
diff --git a/src/main/groovy/io/kamax/mxisd/threepid/notification/email/EmailNotificationHandler.java b/src/main/java/io/kamax/mxisd/threepid/notification/email/EmailNotificationHandler.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/threepid/notification/email/EmailNotificationHandler.java
rename to src/main/java/io/kamax/mxisd/threepid/notification/email/EmailNotificationHandler.java
diff --git a/src/main/groovy/io/kamax/mxisd/threepid/notification/email/IEmailNotificationGenerator.java b/src/main/java/io/kamax/mxisd/threepid/notification/email/IEmailNotificationGenerator.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/threepid/notification/email/IEmailNotificationGenerator.java
rename to src/main/java/io/kamax/mxisd/threepid/notification/email/IEmailNotificationGenerator.java
diff --git a/src/main/groovy/io/kamax/mxisd/threepid/session/IThreePidSession.java b/src/main/java/io/kamax/mxisd/threepid/session/IThreePidSession.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/threepid/session/IThreePidSession.java
rename to src/main/java/io/kamax/mxisd/threepid/session/IThreePidSession.java
diff --git a/src/main/groovy/io/kamax/mxisd/threepid/session/ThreePidSession.java b/src/main/java/io/kamax/mxisd/threepid/session/ThreePidSession.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/threepid/session/ThreePidSession.java
rename to src/main/java/io/kamax/mxisd/threepid/session/ThreePidSession.java
diff --git a/src/main/groovy/io/kamax/mxisd/util/GsonParser.java b/src/main/java/io/kamax/mxisd/util/GsonParser.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/util/GsonParser.java
rename to src/main/java/io/kamax/mxisd/util/GsonParser.java
diff --git a/src/main/groovy/io/kamax/mxisd/util/JsonUtils.java b/src/main/java/io/kamax/mxisd/util/JsonUtils.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/util/JsonUtils.java
rename to src/main/java/io/kamax/mxisd/util/JsonUtils.java
diff --git a/src/main/groovy/io/kamax/mxisd/util/RestClientUtils.java b/src/main/java/io/kamax/mxisd/util/RestClientUtils.java
similarity index 100%
rename from src/main/groovy/io/kamax/mxisd/util/RestClientUtils.java
rename to src/main/java/io/kamax/mxisd/util/RestClientUtils.java