Streamline Backend auth mechanism/return values
This commit is contained in:
46
src/main/groovy/io/kamax/mxisd/UserID.java
Normal file
46
src/main/groovy/io/kamax/mxisd/UserID.java
Normal file
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
47
src/main/groovy/io/kamax/mxisd/UserIdType.java
Normal file
47
src/main/groovy/io/kamax/mxisd/UserIdType.java
Normal file
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
@@ -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<AuthenticatorProvider> 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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);
|
||||
|
||||
}
|
||||
|
@@ -18,16 +18,18 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<ThreePid> threePids = new ArrayList<>();
|
||||
@@ -36,46 +38,51 @@ public class RestAuthReplyJson {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
public void setDisplayName(String displayName) {
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
public List<ThreePid> getThreePids() {
|
||||
return threePids;
|
||||
}
|
||||
}
|
||||
|
||||
public void setThreePids(List<ThreePid> 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;
|
||||
}
|
||||
|
||||
}
|
@@ -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<FirebaseToken>() {
|
||||
@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<UserRecord>() {
|
||||
@@ -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()
|
||||
}
|
||||
|
@@ -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 {
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user