Add mechanisms for 3PID invite expiration and AS integration

- Integration with AS and a fallback user to decline expired invites (#120)
- Rework of the AS feature to make it more independent/re-usable
- Skeleton for admin interface via bot to manage invites (#138)
This commit is contained in:
Max Dor
2019-03-02 03:19:47 +01:00
parent de92e98f7d
commit 254dc5684f
15 changed files with 771 additions and 353 deletions

View File

@@ -0,0 +1,287 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2018 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 <http://www.gnu.org/licenses/>.
*/
package io.kamax.mxisd.config;
import io.kamax.mxisd.Mxisd;
import io.kamax.mxisd.exception.ConfigurationException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
public class AppServiceConfig {
public static class Users {
private String main = "mxisd";
private String inviteExpired = "_mxisd_invite-expired";
public String getMain() {
return main;
}
public void setMain(String main) {
this.main = main;
}
public String getInviteExpired() {
return inviteExpired;
}
public void setInviteExpired(String inviteExpired) {
this.inviteExpired = inviteExpired;
}
public void build() {
// no-op
}
}
public static class Endpoint {
private String url;
private String token;
private transient URL cUrl;
public URL getUrl() {
return cUrl;
}
public void setUrl(String url) {
this.url = url;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public void build() {
if (Objects.isNull(url)) {
return;
}
try {
cUrl = new URL(url);
} catch (MalformedURLException e) {
throw new ConfigurationException("AppService endpoint(s) URL definition");
}
}
}
public static class Endpoints {
private Endpoint toAS = new Endpoint();
private Endpoint toHS = new Endpoint();
public Endpoint getToAS() {
return toAS;
}
public void setToAS(Endpoint toAS) {
this.toAS = toAS;
}
public Endpoint getToHS() {
return toHS;
}
public void setToHS(Endpoint toHS) {
this.toHS = toHS;
}
public void build() {
toAS.build();
toHS.build();
}
}
public static class Synapse {
private String id = "appservice-" + Mxisd.Name;
private String file;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getFile() {
return file;
}
public void setFile(String file) {
this.file = file;
}
public void build() {
// no-op
}
}
public static class Registration {
private Synapse synapse = new Synapse();
public Synapse getSynapse() {
return synapse;
}
public void setSynapse(Synapse synapse) {
this.synapse = synapse;
}
public void build() {
synapse.build();
}
}
public static class AdminFeature {
private Boolean enabled;
private List<String> allowedRoles = new ArrayList<>();
public Boolean getEnabled() {
return enabled;
}
public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}
public List<String> getAllowedRoles() {
return allowedRoles;
}
public void setAllowedRoles(List<String> allowedRoles) {
this.allowedRoles = allowedRoles;
}
public void build() {
// no-op
}
}
public static class Features {
private AdminFeature admin = new AdminFeature();
private Boolean inviteById;
private Boolean cleanExpiredInvite;
public AdminFeature getAdmin() {
return admin;
}
public void setAdmin(AdminFeature admin) {
this.admin = admin;
}
public Boolean getInviteById() {
return inviteById;
}
public void setInviteById(Boolean inviteById) {
this.inviteById = inviteById;
}
public Boolean getCleanExpiredInvite() {
return cleanExpiredInvite;
}
public void setCleanExpiredInvite(Boolean cleanExpiredInvite) {
this.cleanExpiredInvite = cleanExpiredInvite;
}
public void build() {
admin.build();
}
}
private Boolean enabled;
private Features feature = new Features();
private Endpoints endpoint = new Endpoints();
private Registration registration = new Registration();
private Users user = new Users();
public Boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public Features getFeature() {
return feature;
}
public void setFeature(Features feature) {
this.feature = feature;
}
public Endpoints getEndpoint() {
return endpoint;
}
public void setEndpoint(Endpoints endpoint) {
this.endpoint = endpoint;
}
public Registration getRegistration() {
return registration;
}
public void setRegistration(Registration registration) {
this.registration = registration;
}
public Users getUser() {
return user;
}
public void setUser(Users user) {
this.user = user;
}
public void build() {
endpoint.build();
feature.build();
registration.build();
user.build();
}
}

View File

@@ -1,178 +0,0 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2018 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 <http://www.gnu.org/licenses/>.
*/
package io.kamax.mxisd.config;
import io.kamax.mxisd.exception.ConfigurationException;
import org.apache.commons.lang.StringUtils;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
public class ListenerConfig {
public static class Synpase {
private String registrationFile;
public String getRegistrationFile() {
return registrationFile;
}
public void setRegistrationFile(String registrationFile) {
this.registrationFile = registrationFile;
}
}
public static class UserTemplate {
private String type = "regex";
private String template;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getTemplate() {
return template;
}
public void setTemplate(String template) {
this.template = template;
}
}
public static class Token {
private String as;
private String hs;
public String getAs() {
return as;
}
public void setAs(String as) {
this.as = as;
}
public String getHs() {
return hs;
}
public void setHs(String hs) {
this.hs = hs;
}
}
private String id = "appservice-mxisd";
private String url;
private String localpart = "mxisd";
private Token token = new Token();
private List<UserTemplate> users = new ArrayList<>();
private Synpase synapse = new Synpase();
private transient URL csUrl;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public URL getUrl() {
return csUrl;
}
public void setUrl(String url) {
this.url = url;
}
public String getLocalpart() {
return localpart;
}
public void setLocalpart(String localpart) {
this.localpart = localpart;
}
public Token getToken() {
return token;
}
public void setToken(Token token) {
this.token = token;
}
public List<UserTemplate> getUsers() {
return users;
}
public void setUsers(List<UserTemplate> users) {
this.users = users;
}
public Synpase getSynapse() {
return synapse;
}
public void setSynapse(Synpase synapse) {
this.synapse = synapse;
}
public void build() {
try {
if (StringUtils.isBlank(url)) {
return;
}
csUrl = new URL(url);
if (org.apache.commons.lang3.StringUtils.isBlank(getId())) {
throw new IllegalArgumentException("Matrix Listener ID is not set");
}
if (StringUtils.isBlank(getLocalpart())) {
throw new IllegalArgumentException("localpart for matrix listener is not set");
}
if (StringUtils.isBlank(getToken().getAs())) {
throw new IllegalArgumentException("AS token is not set");
}
if (StringUtils.isBlank(getToken().getHs())) {
throw new IllegalArgumentException("HS token is not set");
}
} catch (MalformedURLException e) {
throw new ConfigurationException(e);
}
}
}

View File

@@ -63,7 +63,6 @@ public class MatrixConfig {
private String domain;
private Identity identity = new Identity();
private ListenerConfig listener = new ListenerConfig();
public String getDomain() {
return domain;
@@ -81,14 +80,6 @@ public class MatrixConfig {
this.identity = identity;
}
public ListenerConfig getListener() {
return listener;
}
public void setListener(ListenerConfig listener) {
this.listener = listener;
}
public void build() {
log.info("--- Matrix config ---");
@@ -99,8 +90,6 @@ public class MatrixConfig {
log.info("Domain: {}", getDomain());
log.info("Identity:");
log.info("\tServers: {}", GsonUtil.get().toJson(identity.getServers()));
listener.build();
}
}

View File

@@ -83,6 +83,7 @@ public class MxisdConfig {
}
private AppServiceConfig appsvc = new AppServiceConfig();
private AuthenticationConfig auth = new AuthenticationConfig();
private DirectoryConfig directory = new DirectoryConfig();
private Dns dns = new Dns();
@@ -108,6 +109,14 @@ public class MxisdConfig {
private ViewConfig view = new ViewConfig();
private WordpressConfig wordpress = new WordpressConfig();
public AppServiceConfig getAppsvc() {
return appsvc;
}
public void setAppsvc(AppServiceConfig appsvc) {
this.appsvc = appsvc;
}
public AuthenticationConfig getAuth() {
return auth;
}
@@ -306,6 +315,7 @@ public class MxisdConfig {
log.debug("server.name is empty, using matrix.domain");
}
getAppsvc().build();
getAuth().build();
getDirectory().build();
getExec().build();