From 8b6eadb9abfe0a6a898e28f8833b9337bcdd0131 Mon Sep 17 00:00:00 2001 From: Maxime Dor Date: Sun, 17 Sep 2017 05:17:00 +0200 Subject: [PATCH] Auth endpoint implementation --- .../mxisd/backend/rest/RestAuthProvider.java | 55 ++++++++++++- .../mxisd/backend/rest/RestAuthReplyJson.java | 81 +++++++++++++++++++ .../backend/rest/RestAuthRequestJson.java | 62 ++++++++++++++ .../InvalidResponseJsonException.java | 29 +++++++ .../io/kamax/mxisd/util/GsonParser.java | 73 +++++++++++++++++ .../groovy/io/kamax/mxisd/util/JsonUtils.java | 38 +++++++++ .../io/kamax/mxisd/util/RestClientUtils.java | 44 ++++++++++ 7 files changed, 379 insertions(+), 3 deletions(-) create mode 100644 src/main/groovy/io/kamax/mxisd/backend/rest/RestAuthReplyJson.java create mode 100644 src/main/groovy/io/kamax/mxisd/backend/rest/RestAuthRequestJson.java create mode 100644 src/main/groovy/io/kamax/mxisd/exception/InvalidResponseJsonException.java create mode 100644 src/main/groovy/io/kamax/mxisd/util/GsonParser.java create mode 100644 src/main/groovy/io/kamax/mxisd/util/JsonUtils.java create mode 100644 src/main/groovy/io/kamax/mxisd/util/RestClientUtils.java 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 d519757..57859d7 100644 --- a/src/main/groovy/io/kamax/mxisd/backend/rest/RestAuthProvider.java +++ b/src/main/groovy/io/kamax/mxisd/backend/rest/RestAuthProvider.java @@ -20,16 +20,41 @@ package io.kamax.mxisd.backend.rest; +import com.google.gson.FieldNamingPolicy; +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.config.rest.RestBackendConfig; -import org.apache.commons.lang.NotImplementedException; +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.HttpUriRequest; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import java.io.IOException; + +@Component public class RestAuthProvider implements AuthenticatorProvider { - @Autowired private RestBackendConfig cfg; + private Gson gson; + private GsonParser parser; + private CloseableHttpClient client; + + @Autowired + public RestAuthProvider(RestBackendConfig cfg) { + this.cfg = cfg; + + client = HttpClients.createDefault(); + gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create(); + parser = new GsonParser(gson); + } @Override public boolean isEnabled() { @@ -38,7 +63,31 @@ public class RestAuthProvider implements AuthenticatorProvider { @Override public UserAuthResult authenticate(String id, String password) { - throw new NotImplementedException(); + _MatrixID mxid = new MatrixID(id); + RestAuthRequestJson auth = new RestAuthRequestJson(); + auth.setMxid(id); + auth.setLocalpart(mxid.getLocalPart()); + auth.setDomain(mxid.getDomain()); + auth.setPassword(password); + + 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(); + } + + RestAuthReplyJson reply = parser.parse(res, "auth", RestAuthReplyJson.class); + if (!reply.isSuccess()) { + return result.failure(); + } + + return result.success(reply.getMxid(), reply.getProfile().getDisplayName()); + } catch (IOException e) { + throw new RuntimeException(e); + } } } diff --git a/src/main/groovy/io/kamax/mxisd/backend/rest/RestAuthReplyJson.java b/src/main/groovy/io/kamax/mxisd/backend/rest/RestAuthReplyJson.java new file mode 100644 index 0000000..cb0cef7 --- /dev/null +++ b/src/main/groovy/io/kamax/mxisd/backend/rest/RestAuthReplyJson.java @@ -0,0 +1,81 @@ +/* + * 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.rest; + +import io.kamax.mxisd.ThreePid; + +import java.util.ArrayList; +import java.util.List; + +public class RestAuthReplyJson { + + public static class RestAuthProfileData { + + private String displayName; + private List threePids = new ArrayList<>(); + + public String getDisplayName() { + return displayName; + } + + public void setDisplayName(String displayName) { + this.displayName = displayName; + } + + public List getThreePids() { + return threePids; + } + + public void setThreePids(List threePids) { + this.threePids = threePids; + } + + } + + private Boolean success; + private String mxid; + private RestAuthProfileData profile; + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public String getMxid() { + return mxid; + } + + public void setMxid(String mxid) { + this.mxid = mxid; + } + + public RestAuthProfileData getProfile() { + return profile; + } + + public void setProfile(RestAuthProfileData profile) { + this.profile = profile; + } + +} diff --git a/src/main/groovy/io/kamax/mxisd/backend/rest/RestAuthRequestJson.java b/src/main/groovy/io/kamax/mxisd/backend/rest/RestAuthRequestJson.java new file mode 100644 index 0000000..92d0a5a --- /dev/null +++ b/src/main/groovy/io/kamax/mxisd/backend/rest/RestAuthRequestJson.java @@ -0,0 +1,62 @@ +/* + * 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.rest; + +public class RestAuthRequestJson { + + private String mxid; + private String localpart; + private String domain; + private String password; + + public String getMxid() { + return mxid; + } + + public void setMxid(String mxid) { + this.mxid = mxid; + } + + public String getLocalpart() { + return localpart; + } + + public void setLocalpart(String localpart) { + this.localpart = localpart; + } + + public String getDomain() { + return domain; + } + + public void setDomain(String domain) { + this.domain = domain; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + +} diff --git a/src/main/groovy/io/kamax/mxisd/exception/InvalidResponseJsonException.java b/src/main/groovy/io/kamax/mxisd/exception/InvalidResponseJsonException.java new file mode 100644 index 0000000..fde88e4 --- /dev/null +++ b/src/main/groovy/io/kamax/mxisd/exception/InvalidResponseJsonException.java @@ -0,0 +1,29 @@ +/* + * 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.exception; + +public class InvalidResponseJsonException extends RuntimeException { + + public InvalidResponseJsonException(String s) { + super(s); + } + +} diff --git a/src/main/groovy/io/kamax/mxisd/util/GsonParser.java b/src/main/groovy/io/kamax/mxisd/util/GsonParser.java new file mode 100644 index 0000000..c528f5c --- /dev/null +++ b/src/main/groovy/io/kamax/mxisd/util/GsonParser.java @@ -0,0 +1,73 @@ +/* + * 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.*; +import io.kamax.mxisd.exception.InvalidResponseJsonException; +import org.apache.commons.io.IOUtils; +import org.apache.http.HttpResponse; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; + +public class GsonParser { + + private JsonParser parser = new JsonParser(); + private Gson gson; + + public GsonParser() { + this(new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create()); + } + + public GsonParser(Gson gson) { + this.gson = gson; + } + + public JsonObject parse(InputStream stream, String property) throws IOException { + JsonElement el = parser.parse(IOUtils.toString(stream, StandardCharsets.UTF_8)); + if (!el.isJsonObject()) { + throw new InvalidResponseJsonException("Response body is not a JSON object"); + } + + JsonObject obj = el.getAsJsonObject(); + if (!obj.has(property)) { + throw new IOException("Member " + property + " does not exist"); + } + + el = obj.get(property); + if (!el.isJsonObject()) { + throw new InvalidResponseJsonException("Member " + property + " is not a JSON object"); + } + + return el.getAsJsonObject(); + } + + public T parse(InputStream stream, String memberName, Class type) throws IOException { + JsonObject obj = parse(stream, memberName); + return gson.fromJson(obj, type); + } + + public T parse(HttpResponse res, String memberName, Class type) throws IOException { + return parse(res.getEntity().getContent(), memberName, type); + } + +} diff --git a/src/main/groovy/io/kamax/mxisd/util/JsonUtils.java b/src/main/groovy/io/kamax/mxisd/util/JsonUtils.java new file mode 100644 index 0000000..1edf26a --- /dev/null +++ b/src/main/groovy/io/kamax/mxisd/util/JsonUtils.java @@ -0,0 +1,38 @@ +/* + * 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.Gson; +import com.google.gson.JsonObject; + +public class JsonUtils { + + public static JsonObject getObj(Gson gson, String property, Object value) { + JsonObject obj = new JsonObject(); + obj.add(property, gson.toJsonTree(value)); + return obj; + } + + public static String getObjAsString(Gson gson, String property, Object value) { + return gson.toJson(getObj(gson, property, value)); + } + +} diff --git a/src/main/groovy/io/kamax/mxisd/util/RestClientUtils.java b/src/main/groovy/io/kamax/mxisd/util/RestClientUtils.java new file mode 100644 index 0000000..23eaa2e --- /dev/null +++ b/src/main/groovy/io/kamax/mxisd/util/RestClientUtils.java @@ -0,0 +1,44 @@ +/* + * 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.Gson; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.StringEntity; + +import java.nio.charset.StandardCharsets; + +public class RestClientUtils { + + public static HttpPost post(String url, String body) { + StringEntity entity = new StringEntity(body, StandardCharsets.UTF_8); + entity.setContentType(ContentType.APPLICATION_JSON.toString()); + HttpPost req = new HttpPost(url); + req.setEntity(entity); + return req; + } + + public static HttpPost post(String url, Gson gson, String member, Object o) { + return post(url, JsonUtils.getObjAsString(gson, member, o)); + } + +}