diff --git a/src/main/java/io/kamax/mxisd/ThreePid.java b/src/main/java/io/kamax/mxisd/ThreePid.java index 3a0b6bf..5215c1f 100644 --- a/src/main/java/io/kamax/mxisd/ThreePid.java +++ b/src/main/java/io/kamax/mxisd/ThreePid.java @@ -48,4 +48,22 @@ public class ThreePid { return getMedium() + ":" + getAddress(); } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ThreePid threePid = (ThreePid) o; + + if (!medium.equals(threePid.medium)) return false; + return address.equals(threePid.address); + } + + @Override + public int hashCode() { + int result = medium.hashCode(); + result = 31 * result + address.hashCode(); + return result; + } + } diff --git a/src/main/java/io/kamax/mxisd/auth/AuthManager.java b/src/main/java/io/kamax/mxisd/auth/AuthManager.java index 8ad8edf..8239aaf 100644 --- a/src/main/java/io/kamax/mxisd/auth/AuthManager.java +++ b/src/main/java/io/kamax/mxisd/auth/AuthManager.java @@ -71,14 +71,14 @@ public class AuthManager { continue; } - UserAuthResult authResult = new UserAuthResult().success(mxId, result.getProfile().getDisplayName()); + UserAuthResult authResult = new UserAuthResult().success(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 : authResult.getThreePids()) { log.info("Processing {} for {}", pid, id); - invMgr.publishMappingIfInvited(new ThreePidMapping(pid, authResult.getMxid())); + invMgr.publishMappingIfInvited(new ThreePidMapping(pid, mxId)); } invMgr.lookupMappingsForInvites(); diff --git a/src/main/java/io/kamax/mxisd/auth/UserAuthResult.java b/src/main/java/io/kamax/mxisd/auth/UserAuthResult.java index f51f2bc..a6c8f33 100644 --- a/src/main/java/io/kamax/mxisd/auth/UserAuthResult.java +++ b/src/main/java/io/kamax/mxisd/auth/UserAuthResult.java @@ -20,31 +20,30 @@ package io.kamax.mxisd.auth; -import io.kamax.matrix.ThreePidMedium; import io.kamax.mxisd.ThreePid; -import java.util.ArrayList; import java.util.Collections; -import java.util.List; +import java.util.HashSet; +import java.util.Set; public class UserAuthResult { private boolean success; - private String mxid; private String displayName; - private List threePids = new ArrayList<>(); + private String photo; + private Set threePids = new HashSet<>(); public UserAuthResult failure() { success = false; - mxid = null; displayName = null; + photo = null; + threePids.clear(); return this; } - public UserAuthResult success(String mxid, String displayName) { + public UserAuthResult success(String displayName) { setSuccess(true); - setMxid(mxid); setDisplayName(displayName); return this; @@ -58,14 +57,6 @@ public class UserAuthResult { this.success = success; } - public String getMxid() { - return mxid; - } - - public void setMxid(String mxid) { - this.mxid = mxid; - } - public String getDisplayName() { return displayName; } @@ -74,8 +65,12 @@ public class UserAuthResult { this.displayName = displayName; } - public UserAuthResult withThreePid(ThreePidMedium medium, String address) { - return withThreePid(medium.getId(), address); + public String getPhoto() { + return photo; + } + + public void setPhoto(String photo) { + this.photo = photo; } public UserAuthResult withThreePid(String medium, String address) { @@ -84,8 +79,8 @@ public class UserAuthResult { return this; } - public List getThreePids() { - return Collections.unmodifiableList(threePids); + public Set getThreePids() { + return Collections.unmodifiableSet(threePids); } } diff --git a/src/main/java/io/kamax/mxisd/auth/provider/BackendAuthResult.java b/src/main/java/io/kamax/mxisd/auth/provider/BackendAuthResult.java index 7eb658f..be112f5 100644 --- a/src/main/java/io/kamax/mxisd/auth/provider/BackendAuthResult.java +++ b/src/main/java/io/kamax/mxisd/auth/provider/BackendAuthResult.java @@ -24,21 +24,21 @@ import io.kamax.mxisd.ThreePid; import io.kamax.mxisd.UserID; import io.kamax.mxisd.UserIdType; -import java.util.ArrayList; -import java.util.List; +import java.util.HashSet; +import java.util.Set; public class BackendAuthResult { public static class BackendAuthProfile { private String displayName; - private List threePids = new ArrayList<>(); + private Set threePids = new HashSet<>(); public String getDisplayName() { return displayName; } - public List getThreePids() { + public Set getThreePids() { return threePids; } } diff --git a/src/main/java/io/kamax/mxisd/backend/firebase/GoogleFirebaseAuthenticator.java b/src/main/java/io/kamax/mxisd/backend/firebase/GoogleFirebaseAuthenticator.java index 00bcd66..b67d61a 100644 --- a/src/main/java/io/kamax/mxisd/backend/firebase/GoogleFirebaseAuthenticator.java +++ b/src/main/java/io/kamax/mxisd/backend/firebase/GoogleFirebaseAuthenticator.java @@ -25,6 +25,7 @@ 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.UserInfo; import io.kamax.matrix.ThreePidMedium; import io.kamax.matrix._MatrixID; import io.kamax.mxisd.ThreePid; @@ -139,6 +140,17 @@ public class GoogleFirebaseAuthenticator implements AuthenticatorProvider { result.withThreePid(new ThreePid(ThreePidMedium.PhoneNumber.getId(), user.getPhoneNumber())); } + for (UserInfo info : user.getProviderData()) { + if (StringUtils.isNotBlank(info.getEmail())) { + result.withThreePid(new ThreePid(ThreePidMedium.Email.getId(), info.getEmail())); + } + + if (StringUtils.isNotBlank(info.getPhoneNumber())) { + result.withThreePid(new ThreePid(ThreePidMedium.PhoneNumber.getId(), info.getPhoneNumber())); + } + } + + log.info("Got {} 3PIDs in profile", result.getProfile().getThreePids().size()); } finally { userRecordLatch.countDown(); } diff --git a/src/main/java/io/kamax/mxisd/controller/v1/AuthController.java b/src/main/java/io/kamax/mxisd/controller/v1/AuthController.java index 70dd1bb..ae10c93 100644 --- a/src/main/java/io/kamax/mxisd/controller/v1/AuthController.java +++ b/src/main/java/io/kamax/mxisd/controller/v1/AuthController.java @@ -23,10 +23,12 @@ package io.kamax.mxisd.controller.v1; import com.google.gson.Gson; import com.google.gson.JsonElement; import com.google.gson.JsonObject; -import com.google.gson.JsonParser; import io.kamax.mxisd.auth.AuthManager; import io.kamax.mxisd.auth.UserAuthResult; -import org.apache.commons.io.IOUtils; +import io.kamax.mxisd.controller.v1.io.CredentialsValidationResponse; +import io.kamax.mxisd.exception.JsonMemberNotFoundException; +import io.kamax.mxisd.util.GsonParser; +import io.kamax.mxisd.util.GsonUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -38,7 +40,6 @@ import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletRequest; import java.io.IOException; -import java.nio.charset.StandardCharsets; @RestController @CrossOrigin @@ -47,7 +48,8 @@ public class AuthController { private Logger log = LoggerFactory.getLogger(AuthController.class); - private Gson gson = new Gson(); + private Gson gson = GsonUtil.build(); + private GsonParser parser = new GsonParser(gson); @Autowired private AuthManager mgr; @@ -55,14 +57,9 @@ public class AuthController { @RequestMapping(value = "/_matrix-internal/identity/v1/check_credentials", method = RequestMethod.POST) public String checkCredentials(HttpServletRequest req) { try { - JsonElement el = new JsonParser().parse(IOUtils.toString(req.getInputStream(), StandardCharsets.UTF_8)); - if (!el.isJsonObject() || !el.getAsJsonObject().has("user")) { - throw new IllegalArgumentException("Missing user key"); - } - - JsonObject authData = el.getAsJsonObject().get("user").getAsJsonObject(); + JsonObject authData = parser.parse(req.getInputStream(), "user"); if (!authData.has("id") || !authData.has("password")) { - throw new IllegalArgumentException("Missing id or password keys"); + throw new JsonMemberNotFoundException("Missing id or password keys"); } String id = authData.get("id").getAsString(); @@ -70,16 +67,17 @@ public class AuthController { String password = authData.get("password").getAsString(); UserAuthResult result = mgr.authenticate(id, password); + CredentialsValidationResponse response = new CredentialsValidationResponse(result.isSuccess()); - JsonObject authObj = new JsonObject(); - authObj.addProperty("success", result.isSuccess()); if (result.isSuccess()) { - authObj.addProperty("mxid", result.getMxid()); - authObj.addProperty("display_name", result.getDisplayName()); + response.setDisplayName(result.getDisplayName()); + response.getProfile().setThreePids(result.getThreePids()); } - JsonObject obj = new JsonObject(); + JsonElement authObj = gson.toJsonTree(response); - obj.add("authentication", authObj); + JsonObject obj = new JsonObject(); + obj.add("auth", authObj); + obj.add("authentication", authObj); // TODO remove later, legacy support return gson.toJson(obj); } catch (IOException e) { throw new RuntimeException(e); diff --git a/src/main/java/io/kamax/mxisd/controller/v1/DefaultExceptionHandler.java b/src/main/java/io/kamax/mxisd/controller/v1/DefaultExceptionHandler.java index 19a3407..c606a0e 100644 --- a/src/main/java/io/kamax/mxisd/controller/v1/DefaultExceptionHandler.java +++ b/src/main/java/io/kamax/mxisd/controller/v1/DefaultExceptionHandler.java @@ -22,10 +22,7 @@ package io.kamax.mxisd.controller.v1; import com.google.gson.Gson; import com.google.gson.JsonObject; -import io.kamax.mxisd.exception.BadRequestException; -import io.kamax.mxisd.exception.InternalServerError; -import io.kamax.mxisd.exception.MappingAlreadyExistsException; -import io.kamax.mxisd.exception.MatrixException; +import io.kamax.mxisd.exception.*; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -78,6 +75,18 @@ public class DefaultExceptionHandler { return handle("M_INVALID_BODY", e.getMessage()); } + @ResponseStatus(HttpStatus.BAD_REQUEST) + @ExceptionHandler(InvalidResponseJsonException.class) + public String handle(InvalidResponseJsonException e) { + return handle("M_INVALID_JSON", e.getMessage()); + } + + @ResponseStatus(HttpStatus.BAD_REQUEST) + @ExceptionHandler(JsonMemberNotFoundException.class) + public String handle(JsonMemberNotFoundException e) { + return handle("M_JSON_MISSING_KEYS", e.getMessage()); + } + @ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler(MappingAlreadyExistsException.class) public String handle(MappingAlreadyExistsException e) { diff --git a/src/main/java/io/kamax/mxisd/controller/v1/io/CredentialsValidationResponse.java b/src/main/java/io/kamax/mxisd/controller/v1/io/CredentialsValidationResponse.java new file mode 100644 index 0000000..cd8517a --- /dev/null +++ b/src/main/java/io/kamax/mxisd/controller/v1/io/CredentialsValidationResponse.java @@ -0,0 +1,74 @@ +/* + * 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.io; + +import io.kamax.mxisd.ThreePid; + +import java.util.HashSet; +import java.util.Set; + +public class CredentialsValidationResponse { + + public static class Profile { + + private String displayName; + private Set threePids = new HashSet<>(); + + public String getDisplayName() { + return displayName; + } + + public Set getThreePids() { + return threePids; + } + + public void setThreePids(Set threePids) { + this.threePids = new HashSet<>(threePids); + } + + } + + private boolean success; + private String displayName; // TODO remove later, legacy support + private Profile profile = new Profile(); + + public CredentialsValidationResponse(boolean success) { + this.success = success; + } + + public boolean isSuccess() { + return success; + } + + public String getDisplayName() { + return displayName; + } + + public void setDisplayName(String displayName) { + this.displayName = displayName; + this.profile.displayName = displayName; + } + + public Profile getProfile() { + return profile; + } + +} diff --git a/src/main/java/io/kamax/mxisd/util/GsonUtil.java b/src/main/java/io/kamax/mxisd/util/GsonUtil.java new file mode 100644 index 0000000..ea0c84b --- /dev/null +++ b/src/main/java/io/kamax/mxisd/util/GsonUtil.java @@ -0,0 +1,33 @@ +/* + * 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.util; + +import com.google.gson.FieldNamingPolicy; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +public class GsonUtil { + + public static Gson build() { + return new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create(); + } + +}