diff --git a/src/main/java/io/kamax/mxisd/HttpMxisd.java b/src/main/java/io/kamax/mxisd/HttpMxisd.java index 0911c7c..a4d236a 100644 --- a/src/main/java/io/kamax/mxisd/HttpMxisd.java +++ b/src/main/java/io/kamax/mxisd/HttpMxisd.java @@ -59,6 +59,8 @@ import io.kamax.mxisd.http.undertow.handler.profile.v1.ProfileHandler; import io.kamax.mxisd.http.undertow.handler.register.v1.Register3pidRequestTokenHandler; import io.kamax.mxisd.http.undertow.handler.status.StatusHandler; import io.kamax.mxisd.http.undertow.handler.status.VersionHandler; +import io.kamax.mxisd.http.undertow.handler.term.v2.AcceptTermsHandler; +import io.kamax.mxisd.http.undertow.handler.term.v2.GetTermsHandler; import io.kamax.mxisd.matrix.IdentityServiceAPI; import io.undertow.Handlers; import io.undertow.Undertow; @@ -66,6 +68,7 @@ import io.undertow.server.HttpHandler; import io.undertow.server.RoutingHandler; import io.undertow.util.HttpString; import io.undertow.util.Methods; +import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.List; @@ -143,6 +146,7 @@ public class HttpMxisd { .get(InternalInfoHandler.Path, SaneHandler.around(new InternalInfoHandler())); keyEndpoints(handler); identityEndpoints(handler); + termsEndpoints(handler); httpSrv = Undertow.builder().addHttpListener(m.getConfig().getServer().getPort(), "0.0.0.0").setHandler(handler).build(); httpSrv.start(); @@ -186,6 +190,12 @@ public class HttpMxisd { ); } + private void termsEndpoints(RoutingHandler routingHandler) { + routingHandler.get(GetTermsHandler.PATH, new GetTermsHandler(m.getConfig().getPolicy())); + routingHandler + .post(AcceptTermsHandler.PATH, AuthorizationHandler.around(m.getAccMgr(), sane(new AcceptTermsHandler(m.getAccMgr())))); + } + private void addEndpoints(RoutingHandler routingHandler, HttpString method, boolean useAuthorization, ApiHandler... handlers) { for (ApiHandler handler : handlers) { attachHandler(routingHandler, method, handler, useAuthorization, sane(handler)); @@ -199,23 +209,28 @@ public class HttpMxisd { routingHandler.add(method, apiHandler.getPath(IdentityServiceAPI.V1), httpHandler); } if (matrixConfig.isV2()) { - PolicyConfig policyConfig = m.getConfig().getPolicy(); - List policies = new ArrayList<>(); - if (!policyConfig.getPolicies().isEmpty()) { - for (PolicyConfig.PolicyObject policy : policyConfig.getPolicies().values()) { - for (Pattern pattern : policy.getPatterns()) { - if (pattern.matcher(apiHandler.getHandlerPath()).matches()) { - policies.add(policy); - } - } - } - } - HttpHandler handlerWithTerms = CheckTermsHandler.around(m.getAccMgr(), httpHandler, policies); + HttpHandler handlerWithTerms = CheckTermsHandler.around(m.getAccMgr(), httpHandler, getPolicyObjects(apiHandler)); HttpHandler wrappedHandler = useAuthorization ? AuthorizationHandler.around(m.getAccMgr(), handlerWithTerms) : handlerWithTerms; routingHandler.add(method, apiHandler.getPath(IdentityServiceAPI.V2), wrappedHandler); } } + @NotNull + private List getPolicyObjects(ApiHandler apiHandler) { + PolicyConfig policyConfig = m.getConfig().getPolicy(); + List policies = new ArrayList<>(); + if (!policyConfig.getPolicies().isEmpty()) { + for (PolicyConfig.PolicyObject policy : policyConfig.getPolicies().values()) { + for (Pattern pattern : policy.getPatterns()) { + if (pattern.matcher(apiHandler.getHandlerPath()).matches()) { + policies.add(policy); + } + } + } + } + return policies; + } + private HttpHandler sane(HttpHandler httpHandler) { return SaneHandler.around(httpHandler); } diff --git a/src/main/java/io/kamax/mxisd/auth/AccountManager.java b/src/main/java/io/kamax/mxisd/auth/AccountManager.java index 8715730..9dc82ad 100644 --- a/src/main/java/io/kamax/mxisd/auth/AccountManager.java +++ b/src/main/java/io/kamax/mxisd/auth/AccountManager.java @@ -138,6 +138,7 @@ public class AccountManager { } public void deleteAccount(String token) { + storage.deleteAccepts(token); storage.deleteToken(token); } diff --git a/src/main/java/io/kamax/mxisd/http/undertow/handler/term/v2/AcceptTermsHandler.java b/src/main/java/io/kamax/mxisd/http/undertow/handler/term/v2/AcceptTermsHandler.java new file mode 100644 index 0000000..ae2966b --- /dev/null +++ b/src/main/java/io/kamax/mxisd/http/undertow/handler/term/v2/AcceptTermsHandler.java @@ -0,0 +1,57 @@ +package io.kamax.mxisd.http.undertow.handler.term.v2; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import io.kamax.matrix.json.GsonUtil; +import io.kamax.mxisd.auth.AccountManager; +import io.kamax.mxisd.exception.InvalidCredentialsException; +import io.kamax.mxisd.http.undertow.handler.BasicHttpHandler; +import io.kamax.mxisd.storage.ormlite.dao.AccountDao; +import io.undertow.server.HttpServerExchange; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class AcceptTermsHandler extends BasicHttpHandler { + + private static final Logger LOGGER = LoggerFactory.getLogger(AcceptTermsHandler.class); + + public static final String PATH = "/_matrix/identity/v2/terms"; + + private final AccountManager accountManager; + + public AcceptTermsHandler(AccountManager accountManager) { + this.accountManager = accountManager; + } + + @Override + public void handleRequest(HttpServerExchange exchange) throws Exception { + String token = getAccessToken(exchange); + + JsonObject request = parseJsonObject(exchange); + JsonObject accepts = GsonUtil.getObj(request, "user_accepts"); + AccountDao account = accountManager.findAccount(token); + + if (account == null) { + throw new InvalidCredentialsException(); + } + + if (accepts == null || accepts.isJsonNull()) { + respondJson(exchange, "{}"); + return; + } + + if (accepts.isJsonArray()) { + for (JsonElement acceptItem : accepts.getAsJsonArray()) { + String termUrl = acceptItem.getAsString(); + LOGGER.info("User {} accepts the term: {}", account.getUserId(), termUrl); + accountManager.acceptTerm(token, termUrl); + } + } else { + String termUrl = accepts.getAsString(); + LOGGER.info("User {} accepts the term: {}", account.getUserId(), termUrl); + accountManager.acceptTerm(token, termUrl); + } + + respondJson(exchange, "{}"); + } +} diff --git a/src/main/java/io/kamax/mxisd/http/undertow/handler/term/v2/GetTermsHandler.java b/src/main/java/io/kamax/mxisd/http/undertow/handler/term/v2/GetTermsHandler.java new file mode 100644 index 0000000..75ada58 --- /dev/null +++ b/src/main/java/io/kamax/mxisd/http/undertow/handler/term/v2/GetTermsHandler.java @@ -0,0 +1,37 @@ +package io.kamax.mxisd.http.undertow.handler.term.v2; + +import com.google.gson.JsonObject; +import io.kamax.mxisd.config.PolicyConfig; +import io.kamax.mxisd.http.undertow.handler.BasicHttpHandler; +import io.undertow.server.HttpServerExchange; + +import java.util.Map; + +public class GetTermsHandler extends BasicHttpHandler { + + public static final String PATH = "/_matrix/identity/v2/terms"; + + private final JsonObject policyResponse; + + public GetTermsHandler(PolicyConfig config) { + policyResponse = new JsonObject(); + JsonObject policies = new JsonObject(); + for (Map.Entry policyItem : config.getPolicies().entrySet()) { + JsonObject policy = new JsonObject(); + policy.addProperty("version", policyItem.getValue().getVersion()); + for (Map.Entry termEntry : policyItem.getValue().getTerms().entrySet()) { + JsonObject term = new JsonObject(); + term.addProperty("name", termEntry.getValue().getName()); + term.addProperty("url", termEntry.getValue().getUrl()); + policy.add(termEntry.getKey(), term); + } + policies.add(policyItem.getKey(), policy); + } + policyResponse.add("policies", policies); + } + + @Override + public void handleRequest(HttpServerExchange exchange) throws Exception { + respond(exchange, policyResponse); + } +} diff --git a/src/main/java/io/kamax/mxisd/storage/IStorage.java b/src/main/java/io/kamax/mxisd/storage/IStorage.java index a92c52a..36123d2 100644 --- a/src/main/java/io/kamax/mxisd/storage/IStorage.java +++ b/src/main/java/io/kamax/mxisd/storage/IStorage.java @@ -63,5 +63,7 @@ public interface IStorage { void acceptTerm(String token, String url); + void deleteAccepts(String token); + boolean isTermAccepted(String token, List policies); } diff --git a/src/main/java/io/kamax/mxisd/storage/ormlite/OrmLiteSqlStorage.java b/src/main/java/io/kamax/mxisd/storage/ormlite/OrmLiteSqlStorage.java index ff0b2ae..da91f8a 100644 --- a/src/main/java/io/kamax/mxisd/storage/ormlite/OrmLiteSqlStorage.java +++ b/src/main/java/io/kamax/mxisd/storage/ormlite/OrmLiteSqlStorage.java @@ -294,6 +294,14 @@ public class OrmLiteSqlStorage implements IStorage { }); } + @Override + public void deleteAccepts(String token) { + withCatcher(() -> { + AccountDao account = findAccount(token).orElseThrow(InvalidCredentialsException::new); + acceptedDao.delete(acceptedDao.queryForEq("userId", account.getUserId())); + }); + } + @Override public boolean isTermAccepted(String token, List policies) { return withCatcher(() -> {