Compare commits
3 Commits
v0.3.0-rc.
...
v0.3.0-rc.
Author | SHA1 | Date | |
---|---|---|---|
|
61addd297a | ||
|
1de0951733 | ||
|
d348ebd813 |
@@ -171,10 +171,8 @@ systemctl start mxisd
|
||||
After following the specific instructions to create a config file from the sample:
|
||||
1. Set the `matrix.domain` value to the domain value used in your Home Server configuration
|
||||
2. Set an absolute location for the signing keys using `key.path`
|
||||
3. Configure the E-mail notification sender with items starting with:
|
||||
- `threepid.medium.email.identity`
|
||||
- `threepid.medium.email.connectors.smtp`
|
||||
4. If you would like to support Phone number validation, see the [Twilio configuration](docs/threepids/msisdn/twilio-connector.md)
|
||||
3. Configure the E-mail notification sender following [the documentation](docs/threepids/medium/email/smtp-connector.md)
|
||||
4. If you would like to support Phone number validation, see the [Twilio configuration](docs/threepids/medium/msisdn/twilio-connector.md)
|
||||
|
||||
In case your IS public domain does not match your Matrix domain, see `server.name` and `server.publicUrl`
|
||||
config items.
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -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();
|
||||
|
@@ -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<ThreePid> threePids = new ArrayList<>();
|
||||
private String photo;
|
||||
private Set<ThreePid> 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<ThreePid> getThreePids() {
|
||||
return Collections.unmodifiableList(threePids);
|
||||
public Set<ThreePid> getThreePids() {
|
||||
return Collections.unmodifiableSet(threePids);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -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<ThreePid> threePids = new ArrayList<>();
|
||||
private Set<ThreePid> threePids = new HashSet<>();
|
||||
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
public List<ThreePid> getThreePids() {
|
||||
public Set<ThreePid> getThreePids() {
|
||||
return threePids;
|
||||
}
|
||||
}
|
||||
|
@@ -25,6 +25,9 @@ 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 com.google.i18n.phonenumbers.NumberParseException;
|
||||
import com.google.i18n.phonenumbers.PhoneNumberUtil;
|
||||
import io.kamax.matrix.ThreePidMedium;
|
||||
import io.kamax.matrix._MatrixID;
|
||||
import io.kamax.mxisd.ThreePid;
|
||||
@@ -48,14 +51,7 @@ public class GoogleFirebaseAuthenticator implements AuthenticatorProvider {
|
||||
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();
|
||||
}
|
||||
}
|
||||
private PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
|
||||
|
||||
public GoogleFirebaseAuthenticator(boolean isEnabled) {
|
||||
this.isEnabled = isEnabled;
|
||||
@@ -73,6 +69,42 @@ public class GoogleFirebaseAuthenticator implements AuthenticatorProvider {
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
private void toEmail(BackendAuthResult result, String email) {
|
||||
if (StringUtils.isBlank(email)) {
|
||||
return;
|
||||
}
|
||||
|
||||
result.withThreePid(new ThreePid(ThreePidMedium.Email.getId(), email));
|
||||
}
|
||||
|
||||
private void toMsisdn(BackendAuthResult result, String phoneNumber) {
|
||||
if (StringUtils.isBlank(phoneNumber)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
String number = phoneUtil.format(
|
||||
phoneUtil.parse(
|
||||
phoneNumber,
|
||||
null // No default region
|
||||
),
|
||||
PhoneNumberUtil.PhoneNumberFormat.E164
|
||||
).substring(1); // We want without the leading +
|
||||
result.withThreePid(new ThreePid(ThreePidMedium.PhoneNumber.getId(), number));
|
||||
} catch (NumberParseException e) {
|
||||
log.warn("Invalid phone number: {}", phoneNumber);
|
||||
}
|
||||
}
|
||||
|
||||
private FirebaseCredential getCreds(String credsPath) throws IOException {
|
||||
if (StringUtils.isNotBlank(credsPath)) {
|
||||
return FirebaseCredentials.fromCertificate(new FileInputStream(credsPath));
|
||||
@@ -131,14 +163,15 @@ public class GoogleFirebaseAuthenticator implements AuthenticatorProvider {
|
||||
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()));
|
||||
toEmail(result, user.getEmail());
|
||||
toMsisdn(result, user.getPhoneNumber());
|
||||
|
||||
for (UserInfo info : user.getProviderData()) {
|
||||
toEmail(result, info.getEmail());
|
||||
toMsisdn(result, info.getPhoneNumber());
|
||||
}
|
||||
|
||||
log.info("Got {} 3PIDs in profile", result.getProfile().getThreePids().size());
|
||||
} finally {
|
||||
userRecordLatch.countDown();
|
||||
}
|
||||
|
@@ -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);
|
||||
|
@@ -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) {
|
||||
|
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<ThreePid> threePids = new HashSet<>();
|
||||
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
public Set<ThreePid> getThreePids() {
|
||||
return threePids;
|
||||
}
|
||||
|
||||
public void setThreePids(Set<ThreePid> 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;
|
||||
}
|
||||
|
||||
}
|
33
src/main/java/io/kamax/mxisd/util/GsonUtil.java
Normal file
33
src/main/java/io/kamax/mxisd/util/GsonUtil.java
Normal file
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user