diff --git a/src/main/java/io/kamax/mxisd/HttpMxisd.java b/src/main/java/io/kamax/mxisd/HttpMxisd.java index 362db5d..22683c8 100644 --- a/src/main/java/io/kamax/mxisd/HttpMxisd.java +++ b/src/main/java/io/kamax/mxisd/HttpMxisd.java @@ -31,6 +31,7 @@ import io.kamax.mxisd.http.undertow.handler.auth.v1.LoginHandler; import io.kamax.mxisd.http.undertow.handler.auth.v1.LoginPostHandler; import io.kamax.mxisd.http.undertow.handler.directory.v1.UserDirectorySearchHandler; import io.kamax.mxisd.http.undertow.handler.identity.v1.*; +import io.kamax.mxisd.http.undertow.handler.invite.v1.RoomInviteHandler; import io.kamax.mxisd.http.undertow.handler.profile.v1.InternalProfileHandler; import io.kamax.mxisd.http.undertow.handler.profile.v1.ProfileHandler; import io.kamax.mxisd.http.undertow.handler.register.v1.Register3pidRequestTokenHandler; @@ -101,6 +102,9 @@ public class HttpMxisd { // Registration endpoints .post(Register3pidRequestTokenHandler.Path, SaneHandler.around(new Register3pidRequestTokenHandler(m.getReg(), m.getClientDns(), m.getHttpClient()))) + // Invite endpoints + .post(RoomInviteHandler.Path, SaneHandler.around(new RoomInviteHandler(m.getHttpClient(), m.getClientDns(), m.getInvitationManager()))) + // Application Service endpoints .get("/_matrix/app/v1/users/**", asNotFoundHandler) .get("/users/**", asNotFoundHandler) // Legacy endpoint diff --git a/src/main/java/io/kamax/mxisd/Mxisd.java b/src/main/java/io/kamax/mxisd/Mxisd.java index ea9c3eb..7c0f82a 100644 --- a/src/main/java/io/kamax/mxisd/Mxisd.java +++ b/src/main/java/io/kamax/mxisd/Mxisd.java @@ -109,7 +109,7 @@ public class Mxisd { pMgr = new ProfileManager(ProfileProviders.get(), clientDns, httpClient); notifMgr = new NotificationManager(cfg.getNotification(), NotificationHandlers.get()); sessMgr = new SessionManager(cfg.getSession(), cfg.getMatrix(), store, notifMgr, idStrategy, httpClient); - invMgr = new InvitationManager(cfg, store, idStrategy, keyMgr, signMgr, fedDns, notifMgr); + invMgr = new InvitationManager(cfg, store, idStrategy, keyMgr, signMgr, fedDns, notifMgr, pMgr); authMgr = new AuthManager(cfg, AuthProviders.get(), idStrategy, invMgr, clientDns, httpClient); dirMgr = new DirectoryManager(cfg.getDirectory(), clientDns, httpClient, DirectoryProviders.get()); regMgr = new RegistrationManager(httpClient, clientDns, idStrategy, invMgr); diff --git a/src/main/java/io/kamax/mxisd/backend/sql/SqlProfileProvider.java b/src/main/java/io/kamax/mxisd/backend/sql/SqlProfileProvider.java index bf208c1..1adaf52 100644 --- a/src/main/java/io/kamax/mxisd/backend/sql/SqlProfileProvider.java +++ b/src/main/java/io/kamax/mxisd/backend/sql/SqlProfileProvider.java @@ -23,7 +23,9 @@ package io.kamax.mxisd.backend.sql; import io.kamax.matrix.ThreePid; import io.kamax.matrix._MatrixID; import io.kamax.matrix._ThreePid; +import io.kamax.mxisd.UserIdType; import io.kamax.mxisd.config.sql.SqlConfig; +import io.kamax.mxisd.exception.InternalServerError; import io.kamax.mxisd.profile.ProfileProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -33,16 +35,14 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Optional; public abstract class SqlProfileProvider implements ProfileProvider { - private transient final Logger log = LoggerFactory.getLogger(SqlProfileProvider.class); + private static final Logger log = LoggerFactory.getLogger(SqlProfileProvider.class); private SqlConfig.Profile cfg; - private SqlConnectionPool pool; public SqlProfileProvider(SqlConfig cfg) { @@ -50,6 +50,12 @@ public abstract class SqlProfileProvider implements ProfileProvider { this.pool = new SqlConnectionPool(cfg); } + private void setParameters(PreparedStatement stmt, String value) throws SQLException { + for (int i = 1; i <= stmt.getParameterMetaData().getParameterCount(); i++) { + stmt.setString(i, value); + } + } + @Override public Optional getDisplayName(_MatrixID user) { String stmtSql = cfg.getDisplayName().getQuery(); @@ -94,7 +100,33 @@ public abstract class SqlProfileProvider implements ProfileProvider { @Override public List getRoles(_MatrixID user) { - return Collections.emptyList(); + log.info("Querying roles for {}", user.getId()); + + List roles = new ArrayList<>(); + + String stmtSql = cfg.getRole().getQuery(); + try (Connection conn = pool.get()) { + PreparedStatement stmt = conn.prepareStatement(stmtSql); + if (UserIdType.Localpart.is(cfg.getRole().getType())) { + setParameters(stmt, user.getLocalPart()); + } else if (UserIdType.MatrixID.is(cfg.getRole().getType())) { + setParameters(stmt, user.getId()); + } else { + throw new InternalServerError("Unsupported user type in SQL Role fetching: " + cfg.getRole().getType()); + } + + ResultSet rSet = stmt.executeQuery(); + while (rSet.next()) { + String role = rSet.getString(1); + roles.add(role); + log.debug("Found role {}", role); + } + + log.info("Got {} roles", roles.size()); + return roles; + } catch (SQLException e) { + throw new RuntimeException(e); + } } } diff --git a/src/main/java/io/kamax/mxisd/backend/sql/synapse/SynapseQueries.java b/src/main/java/io/kamax/mxisd/backend/sql/synapse/SynapseQueries.java index c775c66..40f621f 100644 --- a/src/main/java/io/kamax/mxisd/backend/sql/synapse/SynapseQueries.java +++ b/src/main/java/io/kamax/mxisd/backend/sql/synapse/SynapseQueries.java @@ -43,6 +43,10 @@ public class SynapseQueries { return "SELECT medium, address FROM user_threepids WHERE user_id = ?"; } + public static String getRoles() { + return "SELECT DISTINCT(group_id) FROM group_users WHERE user_id = ?"; + } + public static String findByDisplayName(String type, String domain) { if (StringUtils.equals("sqlite", type)) { return "select " + getUserId(type, domain) + ", displayname from profiles p where displayname like ?"; diff --git a/src/main/java/io/kamax/mxisd/backend/sql/synapse/SynapseSqlStoreSupplier.java b/src/main/java/io/kamax/mxisd/backend/sql/synapse/SynapseSqlStoreSupplier.java index 018885a..ef9a331 100644 --- a/src/main/java/io/kamax/mxisd/backend/sql/synapse/SynapseSqlStoreSupplier.java +++ b/src/main/java/io/kamax/mxisd/backend/sql/synapse/SynapseSqlStoreSupplier.java @@ -26,9 +26,13 @@ import io.kamax.mxisd.config.MxisdConfig; import io.kamax.mxisd.directory.DirectoryProviders; import io.kamax.mxisd.lookup.ThreePidProviders; import io.kamax.mxisd.profile.ProfileProviders; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class SynapseSqlStoreSupplier implements IdentityStoreSupplier { + private static final Logger log = LoggerFactory.getLogger(SynapseSqlStoreSupplier.class); + @Override public void accept(Mxisd mxisd) { accept(mxisd.getConfig()); @@ -44,6 +48,7 @@ public class SynapseSqlStoreSupplier implements IdentityStoreSupplier { } if (cfg.getSynapseSql().getProfile().isEnabled()) { + log.debug("Profile is enabled, registering provider"); ProfileProviders.register(() -> new SynapseSqlProfileProvider(cfg.getSynapseSql())); } } diff --git a/src/main/java/io/kamax/mxisd/config/InvitationConfig.java b/src/main/java/io/kamax/mxisd/config/InvitationConfig.java index e814fd6..d776e79 100644 --- a/src/main/java/io/kamax/mxisd/config/InvitationConfig.java +++ b/src/main/java/io/kamax/mxisd/config/InvitationConfig.java @@ -20,13 +20,16 @@ package io.kamax.mxisd.config; -import io.kamax.mxisd.util.GsonUtil; +import io.kamax.matrix.json.GsonUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.ArrayList; +import java.util.List; + public class InvitationConfig { - private transient final Logger log = LoggerFactory.getLogger(InvitationConfig.class); + private static final Logger log = LoggerFactory.getLogger(InvitationConfig.class); public static class Resolution { @@ -51,7 +54,34 @@ public class InvitationConfig { } + public static class SenderPolicy { + + private List hasRole = new ArrayList<>(); + + public List getHasRole() { + return hasRole; + } + + public void setHasRole(List hasRole) { + this.hasRole = hasRole; + } + } + + public static class Policies { + + private SenderPolicy ifSender = new SenderPolicy(); + + public SenderPolicy getIfSender() { + return ifSender; + } + + public void setIfSender(SenderPolicy ifSender) { + this.ifSender = ifSender; + } + } + private Resolution resolution = new Resolution(); + private Policies policy = new Policies(); public Resolution getResolution() { return resolution; @@ -61,9 +91,18 @@ public class InvitationConfig { this.resolution = resolution; } + public Policies getPolicy() { + return policy; + } + + public void setPolicy(Policies policy) { + this.policy = policy; + } + public void build() { log.info("--- Invite config ---"); - log.info("Resolution: {}", GsonUtil.build().toJson(resolution)); + log.info("Resolution: {}", GsonUtil.get().toJson(getResolution())); + log.info("Policies: {}", GsonUtil.get().toJson(getPolicy())); } } diff --git a/src/main/java/io/kamax/mxisd/config/sql/SqlConfig.java b/src/main/java/io/kamax/mxisd/config/sql/SqlConfig.java index e30f5ff..6eb5ed3 100644 --- a/src/main/java/io/kamax/mxisd/config/sql/SqlConfig.java +++ b/src/main/java/io/kamax/mxisd/config/sql/SqlConfig.java @@ -193,11 +193,35 @@ public abstract class SqlConfig { } + public static class ProfileRoles { + + private String type; + private String query; + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getQuery() { + return query; + } + + public void setQuery(String query) { + this.query = query; + } + + } + public static class Profile { private Boolean enabled; private ProfileDisplayName displayName = new ProfileDisplayName(); private ProfileThreepids threepid = new ProfileThreepids(); + private ProfileRoles role = new ProfileRoles(); public Boolean isEnabled() { return enabled; @@ -223,6 +247,14 @@ public abstract class SqlConfig { this.threepid = threepid; } + public ProfileRoles getRole() { + return role; + } + + public void setRole(ProfileRoles role) { + this.role = role; + } + } private boolean enabled; @@ -323,10 +355,11 @@ public abstract class SqlConfig { log.info("3PID mapping query: {}", getIdentity().getQuery()); log.info("Identity medium queries: {}", GsonUtil.build().toJson(getIdentity().getMedium())); log.info("Profile:"); - log.info("\tEnabled: {}", getProfile().isEnabled()); + log.info(" Enabled: {}", getProfile().isEnabled()); if (getProfile().isEnabled()) { - log.info("\tDisplay name query: {}", getProfile().getDisplayName().getQuery()); - log.info("\tProfile 3PID query: {}", getProfile().getThreepid().getQuery()); + log.info(" Display name query: {}", getProfile().getDisplayName().getQuery()); + log.info(" Profile 3PID query: {}", getProfile().getThreepid().getQuery()); + log.info(" Role query: {}", getProfile().getRole().getQuery()); } } } diff --git a/src/main/java/io/kamax/mxisd/config/sql/synapse/SynapseSqlProviderConfig.java b/src/main/java/io/kamax/mxisd/config/sql/synapse/SynapseSqlProviderConfig.java index c6f3281..747a18e 100644 --- a/src/main/java/io/kamax/mxisd/config/sql/synapse/SynapseSqlProviderConfig.java +++ b/src/main/java/io/kamax/mxisd/config/sql/synapse/SynapseSqlProviderConfig.java @@ -20,6 +20,7 @@ package io.kamax.mxisd.config.sql.synapse; +import io.kamax.mxisd.UserIdType; import io.kamax.mxisd.backend.sql.synapse.SynapseQueries; import io.kamax.mxisd.config.sql.SqlConfig; import org.apache.commons.lang.StringUtils; @@ -48,9 +49,17 @@ public class SynapseSqlProviderConfig extends SqlConfig { if (StringUtils.isBlank(getProfile().getDisplayName().getQuery())) { getProfile().getDisplayName().setQuery(SynapseQueries.getDisplayName()); } + if (StringUtils.isBlank(getProfile().getThreepid().getQuery())) { getProfile().getThreepid().setQuery(SynapseQueries.getThreepids()); } + + if (StringUtils.isBlank(getProfile().getRole().getType())) { + getProfile().getRole().setType(UserIdType.MatrixID.getId()); + } + if (StringUtils.isBlank(getProfile().getRole().getQuery())) { + getProfile().getRole().setQuery(SynapseQueries.getRoles()); + } } printConfig(); diff --git a/src/main/java/io/kamax/mxisd/http/undertow/handler/BasicHttpHandler.java b/src/main/java/io/kamax/mxisd/http/undertow/handler/BasicHttpHandler.java index 0b21a19..fa059f8 100644 --- a/src/main/java/io/kamax/mxisd/http/undertow/handler/BasicHttpHandler.java +++ b/src/main/java/io/kamax/mxisd/http/undertow/handler/BasicHttpHandler.java @@ -23,20 +23,29 @@ package io.kamax.mxisd.http.undertow.handler; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import io.kamax.matrix.json.GsonUtil; +import io.kamax.mxisd.dns.ClientDnsOverwrite; +import io.kamax.mxisd.exception.AccessTokenNotFoundException; import io.kamax.mxisd.exception.HttpMatrixException; import io.kamax.mxisd.exception.InternalServerError; import io.kamax.mxisd.proxy.Response; +import io.kamax.mxisd.util.RestClientUtils; import io.undertow.server.HttpHandler; import io.undertow.server.HttpServerExchange; import io.undertow.util.HttpString; import org.apache.commons.io.IOUtils; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.http.Header; +import org.apache.http.HeaderElement; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.impl.client.CloseableHttpClient; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.InetSocketAddress; +import java.net.URI; import java.net.URLDecoder; import java.nio.charset.StandardCharsets; import java.util.Deque; @@ -46,7 +55,19 @@ import java.util.Optional; public abstract class BasicHttpHandler implements HttpHandler { - private transient final Logger log = LoggerFactory.getLogger(BasicHttpHandler.class); + private static final Logger log = LoggerFactory.getLogger(BasicHttpHandler.class); + + protected String getAccessToken(HttpServerExchange exchange) { + return Optional.ofNullable(exchange.getRequestHeaders().getFirst("Authorization")) + .flatMap(v -> { + if (!v.startsWith("Bearer ")) { + return Optional.empty(); + } + + return Optional.of(v.substring("Bearer ".length())); + }).filter(StringUtils::isNotEmpty) + .orElseThrow(AccessTokenNotFoundException::new); + } protected String getRemoteHostAddress(HttpServerExchange exchange) { return ((InetSocketAddress) exchange.getConnection().getPeerAddress()).getAddress().getHostAddress(); @@ -149,4 +170,34 @@ public abstract class BasicHttpHandler implements HttpHandler { upstream.getHeaders().forEach((key, value) -> exchange.getResponseHeaders().addAll(HttpString.tryFromString(key), value)); writeBodyAsUtf8(exchange, upstream.getBody()); } + + protected void proxyPost(HttpServerExchange exchange, JsonObject body, CloseableHttpClient client, ClientDnsOverwrite dns) { + String target = dns.transform(URI.create(exchange.getRequestURL())).toString(); + log.info("Requesting remote: {}", target); + HttpPost req = RestClientUtils.post(target, GsonUtil.get(), body); + + exchange.getRequestHeaders().forEach(header -> { + header.forEach(v -> { + String name = header.getHeaderName().toString(); + if (!StringUtils.startsWithIgnoreCase(name, "content-")) { + req.addHeader(name, v); + } + }); + }); + + try (CloseableHttpResponse res = client.execute(req)) { + exchange.setStatusCode(res.getStatusLine().getStatusCode()); + for (Header h : res.getAllHeaders()) { + for (HeaderElement el : h.getElements()) { + exchange.getResponseHeaders().add(HttpString.tryFromString(h.getName()), el.getValue()); + } + } + res.getEntity().writeTo(exchange.getOutputStream()); + exchange.endExchange(); + } catch (IOException e) { + log.warn("Unable to make proxy call: {}", e.getMessage(), e); + throw new InternalServerError(e); + } + } + } diff --git a/src/main/java/io/kamax/mxisd/http/undertow/handler/invite/v1/RoomInviteHandler.java b/src/main/java/io/kamax/mxisd/http/undertow/handler/invite/v1/RoomInviteHandler.java new file mode 100644 index 0000000..20e3420 --- /dev/null +++ b/src/main/java/io/kamax/mxisd/http/undertow/handler/invite/v1/RoomInviteHandler.java @@ -0,0 +1,103 @@ +/* + * mxisd - Matrix Identity Server Daemon + * Copyright (C) 2019 Kamax Sarl + * + * https://www.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.http.undertow.handler.invite.v1; + +import com.google.gson.JsonObject; +import io.kamax.matrix.MatrixID; +import io.kamax.matrix._MatrixID; +import io.kamax.matrix.json.GsonUtil; +import io.kamax.mxisd.dns.ClientDnsOverwrite; +import io.kamax.mxisd.exception.InternalServerError; +import io.kamax.mxisd.exception.NotAllowedException; +import io.kamax.mxisd.exception.RemoteHomeServerException; +import io.kamax.mxisd.http.undertow.handler.BasicHttpHandler; +import io.kamax.mxisd.invitation.InvitationManager; +import io.undertow.server.HttpServerExchange; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.util.EntityUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.net.URI; +import java.util.Optional; + +public class RoomInviteHandler extends BasicHttpHandler { + + public static final String Path = "/_matrix/client/r0/rooms/{roomId}/invite"; + + private static final Logger log = LoggerFactory.getLogger(RoomInviteHandler.class); + + private final CloseableHttpClient client; + private final ClientDnsOverwrite dns; + private final InvitationManager invMgr; + + public RoomInviteHandler(CloseableHttpClient client, ClientDnsOverwrite dns, InvitationManager invMgr) { + this.client = client; + this.dns = dns; + this.invMgr = invMgr; + } + + @Override + public void handleRequest(HttpServerExchange exchange) { + String accessToken = getAccessToken(exchange); + + String whoamiUri = dns.transform(URI.create(exchange.getRequestURL()).resolve(URI.create("/_matrix/client/r0/account/whoami"))).toString(); + log.info("Who Am I URL: {}", whoamiUri); + HttpGet whoAmIReq = new HttpGet(whoamiUri); + whoAmIReq.addHeader("Authorization", "Bearer " + accessToken); + _MatrixID uId; + try (CloseableHttpResponse whoAmIRes = client.execute(whoAmIReq)) { + int sc = whoAmIRes.getStatusLine().getStatusCode(); + String body = EntityUtils.toString(whoAmIRes.getEntity()); + + if (sc != 200) { + log.warn("Unable to get caller identity from Homeserver - Status code: {}", sc); + log.debug("Body: {}", body); + throw new RemoteHomeServerException(body); + } + + JsonObject json = GsonUtil.parseObj(body); + Optional uIdRaw = GsonUtil.findString(json, "user_id"); + if (!uIdRaw.isPresent()) { + throw new RemoteHomeServerException("No User ID provided when checking identity"); + } + + uId = MatrixID.asAcceptable(uIdRaw.get()); + } catch (IOException e) { + InternalServerError ex = new InternalServerError(e); + log.error("Ref {}: Unable to fetch caller identity from Homeserver", ex.getReference()); + throw ex; + } + + log.info("Processing room invite from {}", uId.getId()); + JsonObject reqBody = parseJsonObject(exchange); + if (!invMgr.canInvite(uId, reqBody)) { + throw new NotAllowedException("Your account is not allowed to invite that address"); + } + + log.info("Invite was allowing, relaying to the Homeserver"); + proxyPost(exchange, reqBody, client, dns); + } + +} diff --git a/src/main/java/io/kamax/mxisd/http/undertow/handler/register/v1/Register3pidRequestTokenHandler.java b/src/main/java/io/kamax/mxisd/http/undertow/handler/register/v1/Register3pidRequestTokenHandler.java index af2dbcb..ed9919c 100644 --- a/src/main/java/io/kamax/mxisd/http/undertow/handler/register/v1/Register3pidRequestTokenHandler.java +++ b/src/main/java/io/kamax/mxisd/http/undertow/handler/register/v1/Register3pidRequestTokenHandler.java @@ -25,26 +25,16 @@ import io.kamax.matrix.ThreePid; import io.kamax.matrix.ThreePidMedium; import io.kamax.matrix.json.GsonUtil; import io.kamax.mxisd.dns.ClientDnsOverwrite; -import io.kamax.mxisd.exception.InternalServerError; import io.kamax.mxisd.exception.NotAllowedException; import io.kamax.mxisd.http.io.identity.SessionEmailTokenRequestJson; import io.kamax.mxisd.http.io.identity.SessionPhoneTokenRequestJson; import io.kamax.mxisd.http.undertow.handler.BasicHttpHandler; import io.kamax.mxisd.registration.RegistrationManager; -import io.kamax.mxisd.util.RestClientUtils; import io.undertow.server.HttpServerExchange; -import io.undertow.util.HttpString; -import org.apache.http.Header; -import org.apache.http.HeaderElement; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpPost; import org.apache.http.impl.client.CloseableHttpClient; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.IOException; -import java.net.URI; - public class Register3pidRequestTokenHandler extends BasicHttpHandler { public static final String Key = "medium"; @@ -77,25 +67,11 @@ public class Register3pidRequestTokenHandler extends BasicHttpHandler { } ThreePid tpid = new ThreePid(medium, address); - if (!mgr.allow(tpid)) { + if (!mgr.isAllowed(tpid)) { throw new NotAllowedException("Your " + medium + " address cannot be used for registration"); } - String target = dns.transform(URI.create(exchange.getRequestURL())).toString(); - log.info("Requesting remote: {}", target); - HttpPost req = RestClientUtils.post(target, GsonUtil.get(), body); - try (CloseableHttpResponse res = client.execute(req)) { - exchange.setStatusCode(res.getStatusLine().getStatusCode()); - for (Header h : res.getAllHeaders()) { - for (HeaderElement el : h.getElements()) { - exchange.getResponseHeaders().add(HttpString.tryFromString(h.getName()), el.getValue()); - } - } - res.getEntity().writeTo(exchange.getOutputStream()); - exchange.endExchange(); - } catch (IOException e) { - throw new InternalServerError(e); - } + proxyPost(exchange, body, client, dns); } } diff --git a/src/main/java/io/kamax/mxisd/invitation/InvitationManager.java b/src/main/java/io/kamax/mxisd/invitation/InvitationManager.java index 36938f3..521ac2d 100644 --- a/src/main/java/io/kamax/mxisd/invitation/InvitationManager.java +++ b/src/main/java/io/kamax/mxisd/invitation/InvitationManager.java @@ -24,6 +24,7 @@ import com.google.gson.JsonArray; import com.google.gson.JsonObject; import io.kamax.matrix.MatrixID; import io.kamax.matrix.ThreePid; +import io.kamax.matrix._MatrixID; import io.kamax.matrix.json.GsonUtil; import io.kamax.mxisd.config.InvitationConfig; import io.kamax.mxisd.config.MxisdConfig; @@ -36,6 +37,7 @@ import io.kamax.mxisd.lookup.SingleLookupReply; import io.kamax.mxisd.lookup.ThreePidMapping; import io.kamax.mxisd.lookup.strategy.LookupStrategy; import io.kamax.mxisd.notification.NotificationManager; +import io.kamax.mxisd.profile.ProfileManager; import io.kamax.mxisd.storage.IStorage; import io.kamax.mxisd.storage.crypto.*; import io.kamax.mxisd.storage.ormlite.dao.ThreePidInviteIO; @@ -78,6 +80,7 @@ public class InvitationManager { private SignatureManager signMgr; private FederationDnsOverwrite dns; private NotificationManager notifMgr; + private ProfileManager profileMgr; private CloseableHttpClient client; private Timer refreshTimer; @@ -91,7 +94,8 @@ public class InvitationManager { KeyManager keyMgr, SignatureManager signMgr, FederationDnsOverwrite dns, - NotificationManager notifMgr + NotificationManager notifMgr, + ProfileManager profileMgr ) { this.cfg = mxisdCfg.getInvite(); this.srvCfg = mxisdCfg.getServer(); @@ -101,6 +105,7 @@ public class InvitationManager { this.signMgr = signMgr; this.dns = dns; this.notifMgr = notifMgr; + this.profileMgr = profileMgr; log.info("Loading saved invites"); Collection ioList = storage.getInvites(); @@ -214,6 +219,30 @@ public class InvitationManager { return lookupMgr.find(medium, address, cfg.getResolution().isRecursive()); } + public boolean canInvite(_MatrixID sender, JsonObject request) { + if (!request.has("medium")) { + log.info("Not a 3PID invite, allowing"); + return true; + } + log.info("3PID invite detected, checking policies..."); + + List allowedRoles = cfg.getPolicy().getIfSender().getHasRole(); + if (Objects.isNull(allowedRoles)) { + log.info("No allowed role configured for sender, allowing"); + return true; + } + + List userRoles = profileMgr.getRoles(sender); + if (Collections.disjoint(userRoles, allowedRoles)) { + log.info("Sender does not have any of the required roles, denying"); + return false; + } + log.info("Sender has at least one of the required roles"); + + log.info("Sender pass all policies to invite, allowing"); + return true; + } + public synchronized IThreePidInviteReply storeInvite(IThreePidInvite invitation) { // TODO better sync if (!notifMgr.isMediumSupported(invitation.getMedium())) { throw new BadRequestException("Medium type " + invitation.getMedium() + " is not supported"); diff --git a/src/main/java/io/kamax/mxisd/registration/RegistrationManager.java b/src/main/java/io/kamax/mxisd/registration/RegistrationManager.java index 63ecbc9..b64613a 100644 --- a/src/main/java/io/kamax/mxisd/registration/RegistrationManager.java +++ b/src/main/java/io/kamax/mxisd/registration/RegistrationManager.java @@ -95,7 +95,7 @@ public class RegistrationManager { } } - public boolean allow(ThreePid tpid) { + public boolean isAllowed(ThreePid tpid) { return invMgr.hasInvite(tpid); }