diff --git a/src/main/groovy/io/kamax/mxisd/UserID.java b/src/main/groovy/io/kamax/mxisd/UserID.java
new file mode 100644
index 0000000..5cf683a
--- /dev/null
+++ b/src/main/groovy/io/kamax/mxisd/UserID.java
@@ -0,0 +1,46 @@
+/*
+ * 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;
+
+// FIXME consider integrating in matrix-java-sdk?
+public class UserID {
+
+ private String type;
+ private String value;
+
+ protected UserID() {
+ // stub for (de)serialization
+ }
+
+ public UserID(String type, String value) {
+ this.type = type;
+ this.value = value;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+}
diff --git a/src/main/groovy/io/kamax/mxisd/UserIdType.java b/src/main/groovy/io/kamax/mxisd/UserIdType.java
new file mode 100644
index 0000000..e0625bf
--- /dev/null
+++ b/src/main/groovy/io/kamax/mxisd/UserIdType.java
@@ -0,0 +1,47 @@
+/*
+ * 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;
+
+import org.apache.commons.lang.StringUtils;
+
+// FIXME consider integrating in matrix-java-sdk?
+public enum UserIdType {
+
+ Localpart("localpart"),
+ MatrixID("mxid"),
+ EmailLocalpart("email_localpart"),
+ Email("email");
+
+ private String id;
+
+ UserIdType(String id) {
+ this.id = id;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public boolean is(String id) {
+ return StringUtils.equalsIgnoreCase(this.id, id);
+ }
+
+}
diff --git a/src/main/groovy/io/kamax/mxisd/auth/AuthManager.java b/src/main/groovy/io/kamax/mxisd/auth/AuthManager.java
index 6dbfff2..aad2cbd 100644
--- a/src/main/groovy/io/kamax/mxisd/auth/AuthManager.java
+++ b/src/main/groovy/io/kamax/mxisd/auth/AuthManager.java
@@ -20,8 +20,12 @@
package io.kamax.mxisd.auth;
+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 io.kamax.mxisd.config.MatrixConfig;
import io.kamax.mxisd.invitation.InvitationManager;
import io.kamax.mxisd.lookup.ThreePidMapping;
import org.slf4j.Logger;
@@ -40,6 +44,9 @@ public class AuthManager {
@Autowired
private List providers = new ArrayList<>();
+ @Autowired
+ private MatrixConfig mxCfg;
+
@Autowired
private InvitationManager invMgr;
@@ -49,17 +56,32 @@ public class AuthManager {
continue;
}
- UserAuthResult result = provider.authenticate(id, password);
+ BackendAuthResult result = provider.authenticate(id, password);
if (result.isSuccess()) {
+
+ String mxId;
+ if (UserIdType.Localpart.is(result.getId().getType())) {
+ mxId = new MatrixID(result.getId().getValue(), mxCfg.getDomain()).getId();
+ } else if (UserIdType.MatrixID.is(result.getId().getType())) {
+ mxId = new MatrixID(result.getId().getValue()).getId();
+ } else {
+ log.warn("Unsupported User ID type {} for backend {}", result.getId().getType(), provider.getClass().getSimpleName());
+ continue;
+ }
+
+ UserAuthResult authResult = new UserAuthResult().success(mxId, result.getProfile().getDisplayName());
+ for (ThreePid pid : result.getProfile().getThreePids()) {
+ authResult.withThreePid(pid.getMedium(), pid.getAddress());
+ }
log.info("{} was authenticated by {}, publishing 3PID mappings, if any", id, provider.getClass().getSimpleName());
- for (ThreePid pid : result.getThreePids()) {
+ for (ThreePid pid : authResult.getThreePids()) {
log.info("Processing {} for {}", pid, id);
- invMgr.publishMappingIfInvited(new ThreePidMapping(pid, result.getMxid()));
+ invMgr.publishMappingIfInvited(new ThreePidMapping(pid, authResult.getMxid()));
}
invMgr.lookupMappingsForInvites();
- return result;
+ return authResult;
}
}
diff --git a/src/main/groovy/io/kamax/mxisd/auth/provider/AuthenticatorProvider.java b/src/main/groovy/io/kamax/mxisd/auth/provider/AuthenticatorProvider.java
index f9aefb6..6103f36 100644
--- a/src/main/groovy/io/kamax/mxisd/auth/provider/AuthenticatorProvider.java
+++ b/src/main/groovy/io/kamax/mxisd/auth/provider/AuthenticatorProvider.java
@@ -20,12 +20,10 @@
package io.kamax.mxisd.auth.provider;
-import io.kamax.mxisd.auth.UserAuthResult;
-
public interface AuthenticatorProvider {
boolean isEnabled();
- UserAuthResult authenticate(String id, String password);
+ BackendAuthResult authenticate(String id, String password);
}
diff --git a/src/main/groovy/io/kamax/mxisd/backend/rest/RestAuthReplyJson.java b/src/main/groovy/io/kamax/mxisd/auth/provider/BackendAuthResult.java
similarity index 52%
rename from src/main/groovy/io/kamax/mxisd/backend/rest/RestAuthReplyJson.java
rename to src/main/groovy/io/kamax/mxisd/auth/provider/BackendAuthResult.java
index cb0cef7..79c1036 100644
--- a/src/main/groovy/io/kamax/mxisd/backend/rest/RestAuthReplyJson.java
+++ b/src/main/groovy/io/kamax/mxisd/auth/provider/BackendAuthResult.java
@@ -18,16 +18,18 @@
* along with this program. If not, see .
*/
-package io.kamax.mxisd.backend.rest;
+package io.kamax.mxisd.auth.provider;
import io.kamax.mxisd.ThreePid;
+import io.kamax.mxisd.UserID;
+import io.kamax.mxisd.UserIdType;
import java.util.ArrayList;
import java.util.List;
-public class RestAuthReplyJson {
+public class BackendAuthResult {
- public static class RestAuthProfileData {
+ public static class BackendAuthProfile {
private String displayName;
private List threePids = new ArrayList<>();
@@ -36,46 +38,51 @@ public class RestAuthReplyJson {
return displayName;
}
- public void setDisplayName(String displayName) {
- this.displayName = displayName;
- }
-
public List getThreePids() {
return threePids;
}
+ }
- public void setThreePids(List threePids) {
- this.threePids = threePids;
- }
+ public static BackendAuthResult failure() {
+ BackendAuthResult r = new BackendAuthResult();
+ r.success = false;
+ return r;
+ }
+ 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;
+
+ return r;
}
private Boolean success;
- private String mxid;
- private RestAuthProfileData profile;
+ private UserID id;
+ private BackendAuthProfile profile = new BackendAuthProfile();
- public boolean isSuccess() {
+ public Boolean isSuccess() {
return success;
}
- public void setSuccess(boolean success) {
- this.success = success;
+ public UserID getId() {
+ return id;
}
- public String getMxid() {
- return mxid;
- }
-
- public void setMxid(String mxid) {
- this.mxid = mxid;
- }
-
- public RestAuthProfileData getProfile() {
+ public BackendAuthProfile getProfile() {
return profile;
}
- public void setProfile(RestAuthProfileData profile) {
- this.profile = profile;
+ public BackendAuthResult withThreePid(ThreePid threePid) {
+ this.profile.threePids.add(threePid);
+
+ return this;
}
}
diff --git a/src/main/groovy/io/kamax/mxisd/backend/firebase/GoogleFirebaseAuthenticator.groovy b/src/main/groovy/io/kamax/mxisd/backend/firebase/GoogleFirebaseAuthenticator.groovy
index ea1d843..7139f2a 100644
--- a/src/main/groovy/io/kamax/mxisd/backend/firebase/GoogleFirebaseAuthenticator.groovy
+++ b/src/main/groovy/io/kamax/mxisd/backend/firebase/GoogleFirebaseAuthenticator.groovy
@@ -27,8 +27,10 @@ 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.auth.UserAuthResult
+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
@@ -49,7 +51,7 @@ public class GoogleFirebaseAuthenticator implements AuthenticatorProvider {
private FirebaseApp fbApp;
private FirebaseAuth fbAuth;
- private void waitOnLatch(UserAuthResult result, CountDownLatch l, long timeout, TimeUnit unit, String purpose) {
+ private void waitOnLatch(BackendAuthResult result, CountDownLatch l, long timeout, TimeUnit unit, String purpose) {
try {
l.await(timeout, unit);
} catch (InterruptedException e) {
@@ -108,22 +110,21 @@ public class GoogleFirebaseAuthenticator implements AuthenticatorProvider {
}
@Override
- public UserAuthResult authenticate(String id, String password) {
+ public BackendAuthResult authenticate(String id, String password) {
if (!isEnabled()) {
throw new IllegalStateException();
}
- final UserAuthResult result = new UserAuthResult();
-
log.info("Trying to authenticate {}", id);
Matcher m = matrixIdLaxPattern.matcher(id);
if (!m.matches()) {
log.warn("Could not validate {} as a Matrix ID", id);
- result.failure();
+ BackendAuthResult.failure();
}
- String localpart = m.group(1);
+ BackendAuthResult result = BackendAuthResult.failure();
+ String localpart = m.group(1);
CountDownLatch l = new CountDownLatch(1);
fbAuth.verifyIdToken(password).addOnSuccessListener(new OnSuccessListener() {
@Override
@@ -131,13 +132,12 @@ public class GoogleFirebaseAuthenticator implements AuthenticatorProvider {
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.failure();
+ result = BackendAuthResult.failure();
return;
}
+ result = BackendAuthResult.success(id, UserIdType.MatrixID, token.getName());
log.info("{} was successfully authenticated", id);
- result.success(id, token.getName());
-
log.info("Fetching profile for {}", id);
CountDownLatch userRecordLatch = new CountDownLatch(1);
fbAuth.getUser(token.getUid()).addOnSuccessListener(new OnSuccessListener() {
@@ -145,12 +145,13 @@ public class GoogleFirebaseAuthenticator implements AuthenticatorProvider {
void onSuccess(UserRecord user) {
try {
if (StringUtils.isNotBlank(user.getEmail())) {
- result.withThreePid(ThreePidMedium.Email, user.getEmail());
+ result.withThreePid(new ThreePid(ThreePidMedium.Email.getId(), user.getEmail()));
}
if (StringUtils.isNotBlank(user.getPhoneNumber())) {
- result.withThreePid(ThreePidMedium.PhoneNumber, user.getPhoneNumber());
+ result.withThreePid(new ThreePid(ThreePidMedium.PhoneNumber.getId(), user.getPhoneNumber()));
}
+
} finally {
userRecordLatch.countDown();
}
@@ -160,7 +161,7 @@ public class GoogleFirebaseAuthenticator implements AuthenticatorProvider {
void onFailure(@NonNull Exception e) {
try {
log.warn("Unable to fetch Firebase user profile for {}", id);
- result.failure();
+ result = BackendAuthResult.failure();
} finally {
userRecordLatch.countDown();
}
@@ -183,7 +184,7 @@ public class GoogleFirebaseAuthenticator implements AuthenticatorProvider {
log.info("Exception", e);
}
- result.failure();
+ result = BackendAuthResult.failure();
} finally {
l.countDown()
}
diff --git a/src/main/groovy/io/kamax/mxisd/backend/ldap/LdapAuthProvider.java b/src/main/groovy/io/kamax/mxisd/backend/ldap/LdapAuthProvider.java
index 30a0fc9..970b379 100644
--- a/src/main/groovy/io/kamax/mxisd/backend/ldap/LdapAuthProvider.java
+++ b/src/main/groovy/io/kamax/mxisd/backend/ldap/LdapAuthProvider.java
@@ -21,8 +21,9 @@
package io.kamax.mxisd.backend.ldap;
import io.kamax.matrix.MatrixID;
-import io.kamax.mxisd.auth.UserAuthResult;
+import io.kamax.mxisd.UserIdType;
import io.kamax.mxisd.auth.provider.AuthenticatorProvider;
+import io.kamax.mxisd.auth.provider.BackendAuthResult;
import org.apache.commons.lang.StringUtils;
import org.apache.directory.api.ldap.model.cursor.CursorException;
import org.apache.directory.api.ldap.model.cursor.CursorLdapReferralException;
@@ -53,7 +54,7 @@ public class LdapAuthProvider extends LdapGenericBackend implements Authenticato
}
@Override
- public UserAuthResult authenticate(String id, String password) {
+ public BackendAuthResult authenticate(String id, String password) {
log.info("Performing auth for {}", id);
LdapConnection conn = getConn();
@@ -88,7 +89,7 @@ public class LdapAuthProvider extends LdapGenericBackend implements Authenticato
conn.bind(entry.getDn(), password);
} catch (LdapException e) {
log.info("Unable to bind using {} because {}", entry.getDn().getName(), e.getMessage());
- return new UserAuthResult().failure();
+ return BackendAuthResult.failure();
}
Attribute nameAttribute = entry.get(getCfg().getAttribute().getName());
@@ -97,7 +98,8 @@ public class LdapAuthProvider extends LdapGenericBackend implements Authenticato
log.info("Authentication successful for {}", entry.getDn().getName());
log.info("DN {} is a valid match", dn);
- return new UserAuthResult().success(mxIdExt.getId(), name);
+ // TODO should we canonicalize the MXID?
+ return BackendAuthResult.success(mxIdExt.getId(), UserIdType.MatrixID, name);
}
} catch (CursorLdapReferralException e) {
log.warn("Entity for {} is only available via referral, skipping", mxIdExt);
@@ -106,7 +108,7 @@ public class LdapAuthProvider extends LdapGenericBackend implements Authenticato
}
log.info("No match were found for {}", id);
- return new UserAuthResult().failure();
+ return BackendAuthResult.failure();
} catch (LdapException | IOException | CursorException e) {
throw new RuntimeException(e);
} finally {
diff --git a/src/main/groovy/io/kamax/mxisd/backend/rest/RestAuthProvider.java b/src/main/groovy/io/kamax/mxisd/backend/rest/RestAuthProvider.java
index 57859d7..8a07c4b 100644
--- a/src/main/groovy/io/kamax/mxisd/backend/rest/RestAuthProvider.java
+++ b/src/main/groovy/io/kamax/mxisd/backend/rest/RestAuthProvider.java
@@ -25,8 +25,8 @@ import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import io.kamax.matrix.MatrixID;
import io.kamax.matrix._MatrixID;
-import io.kamax.mxisd.auth.UserAuthResult;
import io.kamax.mxisd.auth.provider.AuthenticatorProvider;
+import io.kamax.mxisd.auth.provider.BackendAuthResult;
import io.kamax.mxisd.config.rest.RestBackendConfig;
import io.kamax.mxisd.util.GsonParser;
import io.kamax.mxisd.util.RestClientUtils;
@@ -62,7 +62,7 @@ public class RestAuthProvider implements AuthenticatorProvider {
}
@Override
- public UserAuthResult authenticate(String id, String password) {
+ public BackendAuthResult authenticate(String id, String password) {
_MatrixID mxid = new MatrixID(id);
RestAuthRequestJson auth = new RestAuthRequestJson();
auth.setMxid(id);
@@ -72,19 +72,12 @@ public class RestAuthProvider implements AuthenticatorProvider {
HttpUriRequest req = RestClientUtils.post(cfg.getEndpoints().getAuth(), gson, "auth", auth);
try (CloseableHttpResponse res = client.execute(req)) {
- UserAuthResult result = new UserAuthResult();
-
int status = res.getStatusLine().getStatusCode();
if (status < 200 || status >= 300) {
- return result.failure();
+ return BackendAuthResult.failure();
}
- RestAuthReplyJson reply = parser.parse(res, "auth", RestAuthReplyJson.class);
- if (!reply.isSuccess()) {
- return result.failure();
- }
-
- return result.success(reply.getMxid(), reply.getProfile().getDisplayName());
+ return parser.parse(res, "auth", BackendAuthResult.class);
} catch (IOException e) {
throw new RuntimeException(e);
}
diff --git a/src/main/groovy/io/kamax/mxisd/backend/sql/SqlAuthProvider.java b/src/main/groovy/io/kamax/mxisd/backend/sql/SqlAuthProvider.java
index dd9e6ee..17714ab 100644
--- a/src/main/groovy/io/kamax/mxisd/backend/sql/SqlAuthProvider.java
+++ b/src/main/groovy/io/kamax/mxisd/backend/sql/SqlAuthProvider.java
@@ -20,8 +20,8 @@
package io.kamax.mxisd.backend.sql;
-import io.kamax.mxisd.auth.UserAuthResult;
import io.kamax.mxisd.auth.provider.AuthenticatorProvider;
+import io.kamax.mxisd.auth.provider.BackendAuthResult;
import io.kamax.mxisd.config.ServerConfig;
import io.kamax.mxisd.config.sql.SqlProviderConfig;
import io.kamax.mxisd.invitation.InvitationManager;
@@ -50,11 +50,11 @@ public class SqlAuthProvider implements AuthenticatorProvider {
}
@Override
- public UserAuthResult authenticate(String id, String password) {
+ public BackendAuthResult authenticate(String id, String password) {
log.info("Performing dummy authentication try to force invite mapping refresh");
invMgr.lookupMappingsForInvites();
- return new UserAuthResult().failure();
+ return BackendAuthResult.failure();
}
}