Add support for all features for Exec Identity Store

This commit is contained in:
Max Dor
2018-11-01 02:15:56 +01:00
parent b892d19023
commit ded5e3db5e
18 changed files with 875 additions and 122 deletions

View File

@@ -42,7 +42,7 @@ import java.util.Optional;
@Component @Component
public class ExecAuthStore extends ExecStore implements AuthenticatorProvider { public class ExecAuthStore extends ExecStore implements AuthenticatorProvider {
private final transient Logger log = LoggerFactory.getLogger(ExecAuthStore.class); private final Logger log = LoggerFactory.getLogger(ExecAuthStore.class);
private ExecConfig.Auth cfg; private ExecConfig.Auth cfg;
@@ -66,43 +66,43 @@ public class ExecAuthStore extends ExecStore implements AuthenticatorProvider {
ExecAuthResult result = new ExecAuthResult(); ExecAuthResult result = new ExecAuthResult();
result.setId(new UserID(UserIdType.Localpart, uId.getLocalPart())); result.setId(new UserID(UserIdType.Localpart, uId.getLocalPart()));
Processor<ExecAuthResult> process = new Processor<>(); Processor<ExecAuthResult> p = new Processor<>(cfg);
process.withConfig(cfg);
process.addTokenMapper(cfg.getToken().getLocalpart(), uId::getLocalPart);
process.addTokenMapper(cfg.getToken().getDomain(), uId::getDomain);
process.addTokenMapper(cfg.getToken().getMxid(), uId::getId);
process.addTokenMapper(cfg.getToken().getPassword(), () -> password);
process.addInputTemplate(JsonType, tokens -> { p.addTokenMapper(cfg.getToken().getLocalpart(), uId::getLocalPart);
p.addTokenMapper(cfg.getToken().getDomain(), uId::getDomain);
p.addTokenMapper(cfg.getToken().getMxid(), uId::getId);
p.addTokenMapper(cfg.getToken().getPassword(), () -> password);
p.addJsonInputTemplate(tokens -> {
RestAuthRequestJson json = new RestAuthRequestJson(); RestAuthRequestJson json = new RestAuthRequestJson();
json.setLocalpart(tokens.getLocalpart()); json.setLocalpart(tokens.getLocalpart());
json.setDomain(tokens.getDomain()); json.setDomain(tokens.getDomain());
json.setMxid(tokens.getMxid()); json.setMxid(tokens.getMxid());
json.setPassword(tokens.getPassword()); json.setPassword(tokens.getPassword());
return GsonUtil.get().toJson(json); return json;
}); });
process.addInputTemplate(MultilinesType, tokens -> tokens.getLocalpart() + System.lineSeparator() + p.addInputTemplate(MultilinesType, tokens -> tokens.getLocalpart() + System.lineSeparator() +
tokens.getDomain() + System.lineSeparator() + tokens.getDomain() + System.lineSeparator() +
tokens.getMxid() + System.lineSeparator() + tokens.getMxid() + System.lineSeparator() +
tokens.getPassword() + System.lineSeparator() tokens.getPassword() + System.lineSeparator()
); );
process.withExitHandler(pr -> result.setExitStatus(pr.getExitValue())); p.withExitHandler(pr -> result.setExitStatus(pr.getExitValue()));
process.withSuccessHandler(pr -> result.setSuccess(true)); p.withSuccessHandler(pr -> result.setSuccess(true));
process.withSuccessDefault(o -> result); p.withSuccessDefault(o -> result);
process.addSuccessMapper(JsonType, output -> { p.addSuccessMapper(JsonType, output -> {
JsonObject data = GsonUtil.getObj(GsonUtil.parseObj(output), "auth"); JsonObject data = GsonUtil.getObj(GsonUtil.parseObj(output), "auth");
GsonUtil.findPrimitive(data, "success") GsonUtil.findPrimitive(data, "success")
.map(JsonPrimitive::getAsBoolean) .map(JsonPrimitive::getAsBoolean)
.ifPresent(result::setSuccess); .ifPresent(result::setSuccess);
GsonUtil.findObj(data, "profile") GsonUtil.findObj(data, "profile")
.flatMap(p -> GsonUtil.findString(p, "display_name")) .flatMap(profile -> GsonUtil.findString(profile, "display_name"))
.ifPresent(v -> result.getProfile().setDisplayName(v)); .ifPresent(v -> result.getProfile().setDisplayName(v));
return result; return result;
}); });
process.addSuccessMapper(MultilinesType, output -> { p.addSuccessMapper(MultilinesType, output -> {
String[] lines = output.split("\\R"); String[] lines = output.split("\\R");
if (lines.length > 2) { if (lines.length > 2) {
throw new InternalServerError("Exec auth command returned more than 2 lines (" + lines.length + ")"); throw new InternalServerError("Exec auth command returned more than 2 lines (" + lines.length + ")");
@@ -120,10 +120,10 @@ public class ExecAuthStore extends ExecStore implements AuthenticatorProvider {
return result; return result;
}); });
process.withFailureHandler(pr -> result.setSuccess(false)); p.withFailureHandler(pr -> result.setSuccess(false));
process.withFailureDefault(o -> result); p.withFailureDefault(o -> result);
return process.execute(); return p.execute();
} }
} }

View File

@@ -27,6 +27,7 @@ import io.kamax.mxisd.config.MatrixConfig;
import io.kamax.mxisd.controller.directory.v1.io.UserDirectorySearchRequest; import io.kamax.mxisd.controller.directory.v1.io.UserDirectorySearchRequest;
import io.kamax.mxisd.controller.directory.v1.io.UserDirectorySearchResult; import io.kamax.mxisd.controller.directory.v1.io.UserDirectorySearchResult;
import io.kamax.mxisd.directory.IDirectoryProvider; import io.kamax.mxisd.directory.IDirectoryProvider;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@@ -38,7 +39,11 @@ public class ExecDirectoryStore extends ExecStore implements IDirectoryProvider
@Autowired @Autowired
public ExecDirectoryStore(ExecConfig cfg, MatrixConfig mxCfg) { public ExecDirectoryStore(ExecConfig cfg, MatrixConfig mxCfg) {
this.cfg = cfg.getDirectory(); this(cfg.getDirectory(), mxCfg);
}
public ExecDirectoryStore(ExecConfig.Directory cfg, MatrixConfig mxCfg) {
this.cfg = cfg;
this.mxCfg = mxCfg; this.mxCfg = mxCfg;
} }
@@ -48,24 +53,32 @@ public class ExecDirectoryStore extends ExecStore implements IDirectoryProvider
} }
private UserDirectorySearchResult search(ExecConfig.Process cfg, UserDirectorySearchRequest request) { private UserDirectorySearchResult search(ExecConfig.Process cfg, UserDirectorySearchRequest request) {
Processor<UserDirectorySearchResult> processor = new Processor<>(); if (StringUtils.isEmpty(cfg.getCommand())) {
processor.withConfig(cfg); return UserDirectorySearchResult.empty();
processor.addInputTemplate(JsonType, tokens -> GsonUtil.get().toJson(new UserDirectorySearchRequest(tokens.getType(), tokens.getQuery()))); }
processor.addInputTemplate(MultilinesType, tokens -> tokens.getType() + System.lineSeparator() + tokens.getQuery());
processor.addTokenMapper(cfg.getToken().getType(), request::getBy); Processor<UserDirectorySearchResult> p = new Processor<>(cfg);
processor.addTokenMapper(cfg.getToken().getQuery(), request::getSearchTerm);
p.addJsonInputTemplate(tokens -> new UserDirectorySearchRequest(tokens.getType(), tokens.getQuery()));
p.addInputTemplate(MultilinesType, tokens -> tokens.getType() + System.lineSeparator() + tokens.getQuery());
p.addTokenMapper(cfg.getToken().getType(), request::getBy);
p.addTokenMapper(cfg.getToken().getQuery(), request::getSearchTerm);
p.addSuccessMapper(JsonType, output -> {
if (StringUtils.isBlank(output)) {
return UserDirectorySearchResult.empty();
}
processor.addSuccessMapper(JsonType, output -> {
UserDirectorySearchResult response = GsonUtil.get().fromJson(output, UserDirectorySearchResult.class); UserDirectorySearchResult response = GsonUtil.get().fromJson(output, UserDirectorySearchResult.class);
for (UserDirectorySearchResult.Result result : response.getResults()) { for (UserDirectorySearchResult.Result result : response.getResults()) {
result.setUserId(MatrixID.asAcceptable(result.getUserId(), mxCfg.getDomain()).getId()); result.setUserId(MatrixID.asAcceptable(result.getUserId(), mxCfg.getDomain()).getId());
} }
return response; return response;
}); });
processor.withFailureDefault(output -> new UserDirectorySearchResult()); p.withFailureDefault(output -> new UserDirectorySearchResult());
return processor.execute(); return p.execute();
} }
@Override @Override

View File

@@ -21,12 +21,15 @@
package io.kamax.mxisd.backend.exec; package io.kamax.mxisd.backend.exec;
import com.google.gson.JsonArray; import com.google.gson.JsonArray;
import com.google.gson.JsonObject; import com.google.gson.JsonParseException;
import io.kamax.matrix.MatrixID; import io.kamax.matrix.MatrixID;
import io.kamax.matrix.ThreePid; import io.kamax.matrix.ThreePid;
import io.kamax.matrix._MatrixID;
import io.kamax.matrix.json.GsonUtil; import io.kamax.matrix.json.GsonUtil;
import io.kamax.mxisd.UserID;
import io.kamax.mxisd.UserIdType; import io.kamax.mxisd.UserIdType;
import io.kamax.mxisd.backend.rest.LookupBulkResponseJson; import io.kamax.mxisd.backend.rest.LookupBulkResponseJson;
import io.kamax.mxisd.backend.rest.LookupSingleResponseJson;
import io.kamax.mxisd.config.ExecConfig; import io.kamax.mxisd.config.ExecConfig;
import io.kamax.mxisd.config.MatrixConfig; import io.kamax.mxisd.config.MatrixConfig;
import io.kamax.mxisd.exception.InternalServerError; import io.kamax.mxisd.exception.InternalServerError;
@@ -42,6 +45,7 @@ import org.springframework.stereotype.Component;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@@ -55,7 +59,11 @@ public class ExecIdentityStore extends ExecStore implements IThreePidProvider {
@Autowired @Autowired
public ExecIdentityStore(ExecConfig cfg, MatrixConfig mxCfg) { public ExecIdentityStore(ExecConfig cfg, MatrixConfig mxCfg) {
this.cfg = cfg.getIdentity(); this(cfg.getIdentity(), mxCfg);
}
public ExecIdentityStore(ExecConfig.Identity cfg, MatrixConfig mxCfg) {
this.cfg = cfg;
this.mxCfg = mxCfg; this.mxCfg = mxCfg;
} }
@@ -78,46 +86,49 @@ public class ExecIdentityStore extends ExecStore implements IThreePidProvider {
return cfg.getLookup().getSingle(); return cfg.getLookup().getSingle();
} }
private _MatrixID getUserId(UserID id) {
if (Objects.isNull(id)) {
throw new JsonParseException("User id key is not present");
}
if (UserIdType.Localpart.is(id.getType())) {
return MatrixID.asAcceptable(id.getValue(), mxCfg.getDomain());
}
if (UserIdType.MatrixID.is(id.getType())) {
return MatrixID.asAcceptable(id.getValue());
}
throw new InternalServerError("Unknown user type: " + id.getType());
}
@Override @Override
public Optional<SingleLookupReply> find(SingleLookupRequest request) { public Optional<SingleLookupReply> find(SingleLookupRequest request) {
Processor<Optional<SingleLookupReply>> processor = new Processor<>(); Processor<Optional<SingleLookupReply>> p = new Processor<>();
processor.withConfig(cfg.getLookup().getSingle()); p.withConfig(cfg.getLookup().getSingle());
processor.addTokenMapper(getSingleCfg().getToken().getMedium(), request::getType); p.addTokenMapper(getSingleCfg().getToken().getMedium(), request::getType);
processor.addTokenMapper(getSingleCfg().getToken().getAddress(), request::getThreePid); p.addTokenMapper(getSingleCfg().getToken().getAddress(), request::getThreePid);
processor.addInputTemplate(JsonType, tokens -> { p.addJsonInputTemplate(tokens -> new ThreePid(tokens.getMedium(), tokens.getAddress()));
JsonObject json = new JsonObject(); p.addInputTemplate(MultilinesType, tokens -> tokens.getMedium()
json.addProperty("medium", tokens.getMedium());
json.addProperty("address", tokens.getAddress());
return GsonUtil.get().toJson(json);
});
processor.addInputTemplate(MultilinesType, tokens -> tokens.getMedium()
+ System.lineSeparator() + System.lineSeparator()
+ tokens.getAddress() + tokens.getAddress()
); );
processor.addSuccessMapper(JsonType, output -> { p.addSuccessMapper(JsonType, output -> {
if (StringUtils.isBlank(output)) { if (StringUtils.isBlank(output)) {
return Optional.empty(); return Optional.empty();
} }
return GsonUtil.findObj(GsonUtil.parseObj(output), "lookup").map(lookup -> { return GsonUtil.findObj(GsonUtil.parseObj(output), "lookup")
String type = GsonUtil.getStringOrThrow(lookup, "type"); .filter(obj -> !obj.entrySet().isEmpty())
String value = GsonUtil.getStringOrThrow(lookup, "value"); .map(json -> GsonUtil.get().fromJson(json, LookupSingleResponseJson.class))
if (UserIdType.Localpart.is(type)) { .map(lookup -> getUserId(lookup.getId()))
return MatrixID.asAcceptable(value, mxCfg.getDomain()); .map(mxId -> new SingleLookupReply(request, mxId));
}
if (UserIdType.MatrixID.is(type)) {
return MatrixID.asAcceptable(value);
}
throw new InternalServerError("Invalid user type: " + type);
}).map(mxId -> new SingleLookupReply(request, mxId));
}); });
processor.addSuccessMapper(MultilinesType, output -> { p.addSuccessMapper(MultilinesType, output -> {
String[] lines = output.split("\\R"); String[] lines = output.split("\\R");
if (lines.length > 2) { if (lines.length > 2) {
throw new InternalServerError("Exec auth command returned more than 2 lines (" + lines.length + ")"); throw new InternalServerError("Exec auth command returned more than 2 lines (" + lines.length + ")");
@@ -141,23 +152,23 @@ public class ExecIdentityStore extends ExecStore implements IThreePidProvider {
throw new InternalServerError("Invalid user type: " + type); throw new InternalServerError("Invalid user type: " + type);
}); });
processor.withFailureDefault(o -> Optional.empty()); p.withFailureDefault(o -> Optional.empty());
return processor.execute(); return p.execute();
} }
@Override @Override
public List<ThreePidMapping> populate(List<ThreePidMapping> mappings) { public List<ThreePidMapping> populate(List<ThreePidMapping> mappings) {
Processor<List<ThreePidMapping>> processor = new Processor<>(); Processor<List<ThreePidMapping>> p = new Processor<>();
processor.withConfig(cfg.getLookup().getBulk()); p.withConfig(cfg.getLookup().getBulk());
processor.addInput(JsonType, () -> { p.addInput(JsonType, () -> {
JsonArray tpids = GsonUtil.asArray(mappings.stream() JsonArray tpids = GsonUtil.asArray(mappings.stream()
.map(mapping -> GsonUtil.get().toJsonTree(new ThreePid(mapping.getMedium(), mapping.getValue()))) .map(mapping -> GsonUtil.get().toJsonTree(new ThreePid(mapping.getMedium(), mapping.getValue())))
.collect(Collectors.toList())); .collect(Collectors.toList()));
return GsonUtil.get().toJson(GsonUtil.makeObj("lookup", tpids)); return GsonUtil.get().toJson(GsonUtil.makeObj("lookup", tpids));
}); });
processor.addInput(MultilinesType, () -> { p.addInput(MultilinesType, () -> {
StringBuilder input = new StringBuilder(); StringBuilder input = new StringBuilder();
for (ThreePidMapping mapping : mappings) { for (ThreePidMapping mapping : mappings) {
input.append(mapping.getMedium()).append("\t").append(mapping.getValue()).append(System.lineSeparator()); input.append(mapping.getMedium()).append("\t").append(mapping.getValue()).append(System.lineSeparator());
@@ -165,7 +176,7 @@ public class ExecIdentityStore extends ExecStore implements IThreePidProvider {
return input.toString(); return input.toString();
}); });
processor.addSuccessMapper(JsonType, output -> { p.addSuccessMapper(JsonType, output -> {
if (StringUtils.isBlank(output)) { if (StringUtils.isBlank(output)) {
return Collections.emptyList(); return Collections.emptyList();
} }
@@ -190,9 +201,9 @@ public class ExecIdentityStore extends ExecStore implements IThreePidProvider {
}).collect(Collectors.toList()); }).collect(Collectors.toList());
}); });
processor.withFailureDefault(output -> Collections.emptyList()); p.withFailureDefault(output -> Collections.emptyList());
return processor.execute(); return p.execute();
} }
} }

View File

@@ -22,12 +22,16 @@ package io.kamax.mxisd.backend.exec;
import io.kamax.matrix._MatrixID; import io.kamax.matrix._MatrixID;
import io.kamax.matrix._ThreePid; import io.kamax.matrix._ThreePid;
import io.kamax.matrix.json.GsonUtil;
import io.kamax.mxisd.config.ExecConfig; import io.kamax.mxisd.config.ExecConfig;
import io.kamax.mxisd.exception.NotImplementedException; import io.kamax.mxisd.profile.JsonProfileRequest;
import io.kamax.mxisd.profile.JsonProfileResult;
import io.kamax.mxisd.profile.ProfileProvider; import io.kamax.mxisd.profile.ProfileProvider;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
@@ -38,7 +42,11 @@ public class ExecProfileStore extends ExecStore implements ProfileProvider {
@Autowired @Autowired
public ExecProfileStore(ExecConfig cfg) { public ExecProfileStore(ExecConfig cfg) {
this.cfg = cfg.getProfile(); this(cfg.getProfile());
}
public ExecProfileStore(ExecConfig.Profile cfg) {
this.cfg = cfg;
} }
@Override @Override
@@ -46,19 +54,50 @@ public class ExecProfileStore extends ExecStore implements ProfileProvider {
return cfg.isEnabled(); return cfg.isEnabled();
} }
private Optional<JsonProfileResult> getFull(_MatrixID userId, ExecConfig.Process cfg) {
Processor<Optional<JsonProfileResult>> p = new Processor<>(cfg);
p.addJsonInputTemplate(tokens -> new JsonProfileRequest(tokens.getLocalpart(), tokens.getDomain(), tokens.getMxid()));
p.addInputTemplate(MultilinesType, tokens -> tokens.getLocalpart() + System.lineSeparator()
+ tokens.getDomain() + System.lineSeparator()
+ tokens.getMxid() + System.lineSeparator()
);
p.addTokenMapper(cfg.getToken().getLocalpart(), userId::getLocalPart);
p.addTokenMapper(cfg.getToken().getDomain(), userId::getDomain);
p.addTokenMapper(cfg.getToken().getMxid(), userId::getId);
p.withFailureDefault(v -> Optional.empty());
p.addSuccessMapper(JsonType, output -> {
if (StringUtils.isBlank(output)) {
return Optional.empty();
}
return GsonUtil.findObj(GsonUtil.parseObj(output), "profile")
.map(obj -> GsonUtil.get().fromJson(obj, JsonProfileResult.class));
});
return p.execute();
}
@Override @Override
public Optional<String> getDisplayName(_MatrixID userId) { public Optional<String> getDisplayName(_MatrixID userId) {
throw new NotImplementedException(this.getClass().getName()); return getFull(userId, cfg.getDisplayName()).map(JsonProfileResult::getDisplayName);
} }
@Override @Override
public List<_ThreePid> getThreepids(_MatrixID userId) { public List<_ThreePid> getThreepids(_MatrixID userId) {
throw new NotImplementedException(this.getClass().getName()); return getFull(userId, cfg.getThreePid())
.map(p -> Collections.<_ThreePid>unmodifiableList(p.getThreepids()))
.orElseGet(Collections::emptyList);
} }
@Override @Override
public List<String> getRoles(_MatrixID userId) { public List<String> getRoles(_MatrixID userId) {
throw new NotImplementedException(this.getClass().getName()); return getFull(userId, cfg.getRole())
.map(JsonProfileResult::getRoles)
.orElseGet(Collections::emptyList);
} }
} }

View File

@@ -20,6 +20,7 @@
package io.kamax.mxisd.backend.exec; package io.kamax.mxisd.backend.exec;
import io.kamax.matrix.json.GsonUtil;
import io.kamax.mxisd.config.ExecConfig; import io.kamax.mxisd.config.ExecConfig;
import io.kamax.mxisd.exception.InternalServerError; import io.kamax.mxisd.exception.InternalServerError;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
@@ -42,8 +43,18 @@ public class ExecStore {
public static final String JsonType = "json"; public static final String JsonType = "json";
public static final String MultilinesType = "multilines"; public static final String MultilinesType = "multilines";
protected static String toJson(Object o) {
return GsonUtil.get().toJson(o);
}
private final Logger log = LoggerFactory.getLogger(ExecStore.class); private final Logger log = LoggerFactory.getLogger(ExecStore.class);
private Supplier<ProcessExecutor> executorSupplier = () -> new ProcessExecutor().readOutput(true);
public void setExecutorSupplier(Supplier<ProcessExecutor> supplier) {
executorSupplier = supplier;
}
public class Processor<V> { public class Processor<V> {
private ExecConfig.Process cfg; private ExecConfig.Process cfg;
@@ -55,7 +66,6 @@ public class ExecStore {
private Map<String, Function<ExecConfig.TokenOverride, String>> inputTypeTemplates; private Map<String, Function<ExecConfig.TokenOverride, String>> inputTypeTemplates;
private Supplier<String> inputTypeNoTemplateHandler; private Supplier<String> inputTypeNoTemplateHandler;
private Map<String, Function<ExecConfig.TokenOverride, String>> inputTypeTokenizers;
private Map<String, Supplier<String>> tokenMappers; private Map<String, Supplier<String>> tokenMappers;
private Function<String, String> tokenHandler; private Function<String, String> tokenHandler;
@@ -70,17 +80,22 @@ public class ExecStore {
private Map<String, Function<String, V>> unknownMappers; private Map<String, Function<String, V>> unknownMappers;
private Function<String, V> unknownDefault; private Function<String, V> unknownDefault;
public Processor(ExecConfig.Process cfg) {
this();
withConfig(cfg);
}
public Processor() { public Processor() {
tokenMappers = new HashMap<>(); tokenMappers = new HashMap<>();
inputTypeSuppliers = new HashMap<>(); inputTypeSuppliers = new HashMap<>();
inputTypeTemplates = new HashMap<>(); inputTypeTemplates = new HashMap<>();
tokenHandler = input -> { withTokenHandler(tokenHandler = input -> {
for (Map.Entry<String, Supplier<String>> entry : tokenMappers.entrySet()) { for (Map.Entry<String, Supplier<String>> entry : tokenMappers.entrySet()) {
input = input.replace(entry.getKey(), entry.getValue().get()); input = input.replace(entry.getKey(), entry.getValue().get());
} }
return input; return input;
}; });
inputTypeNoTemplateHandler = () -> cfg.getInput().getType() inputTypeNoTemplateHandler = () -> cfg.getInput().getType()
.map(type -> inputTypeTemplates.get(type).apply(cfg.getToken())) .map(type -> inputTypeTemplates.get(type).apply(cfg.getToken()))
@@ -98,12 +113,12 @@ public class ExecStore {
inputSupplier = () -> cfg.getInput().getType().map(type -> inputTypeMapper.apply(type)); inputSupplier = () -> cfg.getInput().getType().map(type -> inputTypeMapper.apply(type));
onExitHandler = pr -> { withExitHandler(pr -> {
}; });
successMappers = new HashMap<>();
successHandler = pr -> { successHandler = pr -> {
}; };
successMappers = new HashMap<>();
successDefault = output -> { successDefault = output -> {
log.info("{} stdout: {}{}", cfg.getCommand(), System.lineSeparator(), output); log.info("{} stdout: {}{}", cfg.getCommand(), System.lineSeparator(), output);
throw new InternalServerError("Exec command has no success handler configured. This is a bug. Please report."); throw new InternalServerError("Exec command has no success handler configured. This is a bug. Please report.");
@@ -119,90 +134,80 @@ public class ExecStore {
unknownHandler = pr -> log.warn("Unexpected exit status: {}", pr.getExitValue()); unknownHandler = pr -> log.warn("Unexpected exit status: {}", pr.getExitValue());
unknownMappers = new HashMap<>(); unknownMappers = new HashMap<>();
unknownDefault = output -> { withUnknownDefault(output -> {
log.error("{} stdout:{}{}", cfg.getCommand(), System.lineSeparator(), output); log.error("{} stdout:{}{}", cfg.getCommand(), System.lineSeparator(), output);
throw new InternalServerError("Exec command returned with unexpected exit status"); throw new InternalServerError("Exec command returned with unexpected exit status");
}; });
} }
public Processor<V> withConfig(ExecConfig.Process cfg) { public void withConfig(ExecConfig.Process cfg) {
this.cfg = cfg; this.cfg = cfg;
return this;
} }
public Processor<V> addTokenMapper(String token, Supplier<String> data) { public void addTokenMapper(String token, Supplier<String> data) {
tokenMappers.put(token, data); tokenMappers.put(token, data);
return this;
} }
public Processor<V> withTokenHandler(Function<String, String> tokenHandler) { public void withTokenHandler(Function<String, String> handler) {
this.tokenHandler = tokenHandler; tokenHandler = handler;
return this;
} }
public Processor<V> addInput(String type, Supplier<String> handler) { public void addInput(String type, Supplier<String> handler) {
inputTypeSuppliers.put(type, handler); inputTypeSuppliers.put(type, handler);
return this;
} }
public Processor<V> addInputTemplate(String type, Function<ExecConfig.TokenOverride, String> template) { protected void addInputTemplate(String type, Function<ExecConfig.TokenOverride, String> template) {
inputTypeTemplates.put(type, template); inputTypeTemplates.put(type, template);
return this;
} }
public Processor<V> withExitHandler(Consumer<ProcessResult> handler) { public void addJsonInputTemplate(Function<ExecConfig.TokenOverride, Object> template) {
inputTypeTemplates.put(JsonType, token -> GsonUtil.get().toJson(template.apply(token)));
}
public void withExitHandler(Consumer<ProcessResult> handler) {
onExitHandler = handler; onExitHandler = handler;
return this;
} }
public Processor<V> withSuccessHandler(Consumer<ProcessResult> handler) { public void withSuccessHandler(Consumer<ProcessResult> handler) {
successHandler = handler; successHandler = handler;
return this;
} }
public Processor<V> addSuccessMapper(String type, Function<String, V> mapper) { public void addSuccessMapper(String type, Function<String, V> mapper) {
successMappers.put(type, mapper); successMappers.put(type, mapper);
return this;
} }
public Processor<V> withSuccessDefault(Function<String, V> mapper) { public void withSuccessDefault(Function<String, V> mapper) {
successDefault = mapper; successDefault = mapper;
return this;
} }
public Processor<V> withFailureHandler(Consumer<ProcessResult> handler) { public void withFailureHandler(Consumer<ProcessResult> handler) {
failureHandler = handler; failureHandler = handler;
return this;
} }
public Processor<V> addFailureMapper(String type, Function<String, V> mapper) { public void addFailureMapper(String type, Function<String, V> mapper) {
failureMappers.put(type, mapper); failureMappers.put(type, mapper);
return this;
} }
public Processor<V> withFailureDefault(Function<String, V> mapper) { public void withFailureDefault(Function<String, V> mapper) {
failureDefault = mapper; failureDefault = mapper;
return this;
} }
public Processor<V> addUnknownMapper(String type, Function<String, V> mapper) { public void addUnknownMapper(String type, Function<String, V> mapper) {
unknownMappers.put(type, mapper); unknownMappers.put(type, mapper);
return this;
} }
public Processor<V> withUnknownDefault(Function<String, V> mapper) { public void withUnknownDefault(Function<String, V> mapper) {
unknownDefault = mapper; unknownDefault = mapper;
return this;
} }
V execute() { public V execute() {
log.info("Executing {}", cfg.getCommand()); log.info("Executing {}", cfg.getCommand());
try { try {
ProcessExecutor psExec = new ProcessExecutor().readOutput(true); ProcessExecutor psExec = executorSupplier.get();
List<String> args = new ArrayList<>(); List<String> args = new ArrayList<>();
args.add(cfg.getCommand()); args.add(tokenHandler.apply(cfg.getCommand()));
args.addAll(cfg.getArgs().stream().map(arg -> tokenHandler.apply(arg)).collect(Collectors.toList())); args.addAll(cfg.getArgs().stream().map(arg -> tokenHandler.apply(arg)).collect(Collectors.toList()));
psExec.command(args); psExec.command(args);
@@ -235,8 +240,9 @@ public class ExecStore {
.map(type -> unknownMappers.getOrDefault(type, unknownDefault).apply(output)) .map(type -> unknownMappers.getOrDefault(type, unknownDefault).apply(output))
.orElseGet(() -> unknownDefault.apply(output)); .orElseGet(() -> unknownDefault.apply(output));
} }
} catch (IOException | InterruptedException | TimeoutException e) { } catch (RuntimeException | IOException | InterruptedException | TimeoutException e) {
log.error("Failed to execute {}", cfg.getCommand()); log.error("Failed to execute {}", cfg.getCommand());
log.debug("Internal exception:", e);
throw new InternalServerError(e); throw new InternalServerError(e);
} }
} }

View File

@@ -20,6 +20,7 @@
package io.kamax.mxisd.backend.rest; package io.kamax.mxisd.backend.rest;
import io.kamax.matrix.ThreePidMedium;
import io.kamax.mxisd.UserID; import io.kamax.mxisd.UserID;
public class LookupSingleResponseJson { public class LookupSingleResponseJson {
@@ -32,12 +33,28 @@ public class LookupSingleResponseJson {
return medium; return medium;
} }
public void setMedium(String medium) {
this.medium = medium;
}
public void setMedium(ThreePidMedium medium) {
setMedium(medium.getId());
}
public String getAddress() { public String getAddress() {
return address; return address;
} }
public void setAddress(String address) {
this.address = address;
}
public UserID getId() { public UserID getId() {
return id; return id;
} }
public void setId(UserID id) {
this.id = id;
}
} }

View File

@@ -420,9 +420,12 @@ public class ExecConfig {
} }
public class Profile extends Process { public class Profile {
private Boolean enabled; private Boolean enabled;
private Process displayName = new Process();
private Process threePid = new Process();
private Process role = new Process();
public Boolean isEnabled() { public Boolean isEnabled() {
return enabled; return enabled;
@@ -432,6 +435,30 @@ public class ExecConfig {
this.enabled = enabled; this.enabled = enabled;
} }
public Process getDisplayName() {
return displayName;
}
public void setDisplayName(Process displayName) {
this.displayName = displayName;
}
public Process getThreePid() {
return threePid;
}
public void setThreePid(Process threePid) {
this.threePid = threePid;
}
public Process getRole() {
return role;
}
public void setRoles(Process role) {
this.role = role;
}
} }
private boolean enabled; private boolean enabled;
@@ -490,7 +517,7 @@ public class ExecConfig {
} }
@PostConstruct @PostConstruct
public void build() { public ExecConfig compute() {
if (Objects.isNull(getAuth().isEnabled())) { if (Objects.isNull(getAuth().isEnabled())) {
getAuth().setEnabled(isEnabled()); getAuth().setEnabled(isEnabled());
} }
@@ -506,6 +533,8 @@ public class ExecConfig {
if (Objects.isNull(getProfile().isEnabled())) { if (Objects.isNull(getProfile().isEnabled())) {
getProfile().setEnabled(isEnabled()); getProfile().setEnabled(isEnabled());
} }
return this;
} }
} }

View File

@@ -25,6 +25,10 @@ import java.util.Set;
public class UserDirectorySearchResult { public class UserDirectorySearchResult {
public static UserDirectorySearchResult empty() {
return new UserDirectorySearchResult();
}
public static class Result { public static class Result {
private String displayName; private String displayName;

View File

@@ -0,0 +1,67 @@
/*
* 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.profile;
import io.kamax.matrix._MatrixID;
public class JsonProfileRequest {
private String localpart;
private String domain;
private String mxid;
public JsonProfileRequest(_MatrixID mxId) {
this.localpart = mxId.getLocalPart();
this.domain = mxId.getDomain();
this.mxid = mxId.getId();
}
public JsonProfileRequest(String localpart, String domain, String mxId) {
this.localpart = localpart;
this.domain = domain;
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 getMxid() {
return mxid;
}
public void setMxid(String mxid) {
this.mxid = mxid;
}
}

View File

@@ -0,0 +1,69 @@
/*
* 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.profile;
import io.kamax.matrix.ThreePid;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
public class JsonProfileResult {
private String displayName;
private List<ThreePid> threepids;
private List<String> roles;
public String getDisplayName() {
return displayName;
}
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
public List<ThreePid> getThreepids() {
return threepids;
}
public void setThreepids(List<ThreePid> threepids) {
this.threepids = threepids;
}
public void addThreepid(ThreePid threepid) {
if (Objects.isNull(threepids)) threepids = new ArrayList<>();
threepids.add(threepid);
}
public List<String> getRoles() {
return roles;
}
public void setRoles(List<String> roles) {
this.roles = roles;
}
public void addRole(String role) {
if (Objects.isNull(roles)) roles = new ArrayList<>();
roles.add(role);
}
}

View File

@@ -18,13 +18,11 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package io.kamax.mxisd.backend.exec.auth.input; package io.kamax.mxisd.backend.exec;
import io.kamax.matrix.MatrixID; import io.kamax.matrix.MatrixID;
import io.kamax.matrix._MatrixID; import io.kamax.matrix._MatrixID;
import io.kamax.mxisd.UserIdType; import io.kamax.mxisd.UserIdType;
import io.kamax.mxisd.backend.exec.ExecAuthResult;
import io.kamax.mxisd.backend.exec.ExecAuthStore;
import io.kamax.mxisd.config.ExecConfig; import io.kamax.mxisd.config.ExecConfig;
import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.RandomStringUtils;
import org.junit.Before; import org.junit.Before;
@@ -35,7 +33,7 @@ import java.util.Collections;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
public abstract class ExecAuthTest { public abstract class ExecAuthStoreTest {
protected final ExecConfig cfg; protected final ExecConfig cfg;
protected final ExecAuthStore p; protected final ExecAuthStore p;
@@ -90,7 +88,7 @@ public abstract class ExecAuthTest {
cfg.getAuth().addEnv("REQ_PASS", requiredPass); cfg.getAuth().addEnv("REQ_PASS", requiredPass);
} }
public ExecAuthTest() { public ExecAuthStoreTest() {
cfg = new ExecConfig(); cfg = new ExecConfig();
p = new ExecAuthStore(cfg); p = new ExecAuthStore(cfg);
} }

View File

@@ -0,0 +1,128 @@
/*
* 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.backend.exec;
import io.kamax.matrix.MatrixID;
import io.kamax.matrix.json.GsonUtil;
import io.kamax.mxisd.config.ExecConfig;
import io.kamax.mxisd.controller.directory.v1.io.UserDirectorySearchResult;
import io.kamax.mxisd.exception.InternalServerError;
import org.apache.commons.lang3.StringUtils;
import org.junit.Test;
import java.util.Collections;
import static junit.framework.TestCase.assertTrue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
public class ExecDirectoryStoreTest extends ExecStoreTest {
public ExecDirectoryStoreTest() {
executables.put("byNameSuccessEmptyResult", () ->
make(0, () ->
GsonUtil.get().toJson(UserDirectorySearchResult.empty())
)
);
executables.put("byNameSuccessSingleResult", () -> make(0, () -> {
UserDirectorySearchResult.Result resultIo = new UserDirectorySearchResult.Result();
resultIo.setUserId(user1Localpart);
resultIo.setDisplayName(user1Name);
UserDirectorySearchResult io = new UserDirectorySearchResult();
io.setLimited(false);
io.setResults(Collections.singleton(resultIo));
return GsonUtil.get().toJson(io);
}));
}
private ExecConfig.Directory getCfg() {
ExecConfig.Directory cfg = new ExecConfig().compute().getDirectory();
assertFalse(cfg.isEnabled());
cfg.setEnabled(true);
assertTrue(cfg.isEnabled());
cfg.getSearch().getByName().getOutput().setType(ExecStore.JsonType);
return cfg;
}
private ExecDirectoryStore getStore(ExecConfig.Directory cfg) {
ExecDirectoryStore store = new ExecDirectoryStore(cfg, getMatrixCfg());
store.setExecutorSupplier(this::build);
assertTrue(store.isEnabled());
return store;
}
private ExecDirectoryStore getStore(String command) {
ExecConfig.Directory cfg = getCfg();
cfg.getSearch().getByName().setCommand(command);
cfg.getSearch().getByThreepid().setCommand(command);
return getStore(cfg);
}
@Test
public void byNameNoCommandDefined() {
ExecConfig.Directory cfg = getCfg();
assertTrue(StringUtils.isEmpty(cfg.getSearch().getByName().getCommand()));
ExecDirectoryStore store = getStore(cfg);
UserDirectorySearchResult result = store.searchByDisplayName("user");
assertFalse(result.isLimited());
assertTrue(result.getResults().isEmpty());
}
@Test
public void byNameSuccessNoOutput() {
UserDirectorySearchResult result = getStore(sno).searchByDisplayName("user");
assertFalse(result.isLimited());
assertTrue(result.getResults().isEmpty());
}
@Test
public void byNameSuccessEmptyResult() {
UserDirectorySearchResult output = getStore("byNameSuccessEmptyResult").searchByDisplayName("user");
assertFalse(output.isLimited());
assertTrue(output.getResults().isEmpty());
}
@Test
public void byNameSuccessSingleResult() {
UserDirectorySearchResult output = getStore("byNameSuccessSingleResult").searchByDisplayName("user");
assertFalse(output.isLimited());
assertEquals(1, output.getResults().size());
UserDirectorySearchResult.Result result = output.getResults().iterator().next();
assertEquals(MatrixID.from(user1Localpart, domain).acceptable().getId(), result.getUserId());
assertEquals(user1Name, result.getDisplayName());
}
@Test
public void byNameFailureNoOutput() {
UserDirectorySearchResult result = getStore(fno).searchByDisplayName("user");
assertFalse(result.isLimited());
assertTrue(result.getResults().isEmpty());
}
@Test(expected = InternalServerError.class)
public void byNameUnknownNoOutput() {
getStore(uno).searchByDisplayName("user");
}
}

View File

@@ -0,0 +1,129 @@
/*
* 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.backend.exec;
import com.google.gson.JsonObject;
import io.kamax.matrix.MatrixID;
import io.kamax.matrix.ThreePidMedium;
import io.kamax.matrix.json.GsonUtil;
import io.kamax.mxisd.UserID;
import io.kamax.mxisd.UserIdType;
import io.kamax.mxisd.backend.rest.LookupSingleResponseJson;
import io.kamax.mxisd.config.ExecConfig;
import io.kamax.mxisd.exception.InternalServerError;
import io.kamax.mxisd.lookup.SingleLookupReply;
import io.kamax.mxisd.lookup.SingleLookupRequest;
import org.junit.Test;
import java.util.Optional;
import static junit.framework.TestCase.assertTrue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
public class ExecIdentityStoreTest extends ExecStoreTest {
public ExecIdentityStoreTest() {
executables.put("singleSuccessEmpty", () -> make(0, () ->
GsonUtil.get().toJson(GsonUtil.makeObj("lookup", new JsonObject()))));
executables.put("singleSuccessData", () -> makeJson(0, () -> {
LookupSingleResponseJson json = new LookupSingleResponseJson();
json.setMedium(ThreePidMedium.Email);
json.setAddress(user1Email);
json.setId(new UserID(UserIdType.Localpart, user1Localpart));
return GsonUtil.makeObj("lookup", GsonUtil.get().toJsonTree(json));
}));
executables.put("singleSuccessEmptyFromInvalidOutput", () -> makeJson(0, () -> {
JsonObject lookup = new JsonObject();
lookup.addProperty("medium", "");
return GsonUtil.makeObj("lookup", lookup);
}));
}
private ExecConfig.Identity getCfg() {
ExecConfig.Identity cfg = new ExecConfig().compute().getIdentity();
assertFalse(cfg.isEnabled());
cfg.setEnabled(true);
assertTrue(cfg.isEnabled());
cfg.getLookup().getSingle().getOutput().setType(ExecStore.JsonType);
cfg.getLookup().getBulk().getOutput().setType(ExecStore.JsonType);
return cfg;
}
private ExecIdentityStore getStore(ExecConfig.Identity cfg) {
ExecIdentityStore store = new ExecIdentityStore(cfg, getMatrixCfg());
store.setExecutorSupplier(this::build);
assertTrue(store.isEnabled());
return store;
}
private ExecIdentityStore getStore(String command) {
ExecConfig.Identity cfg = getCfg();
cfg.getLookup().getSingle().setCommand(command);
cfg.getLookup().getBulk().setCommand(command);
return getStore(cfg);
}
@Test
public void singleSuccessNoOutput() {
ExecIdentityStore store = getStore(sno);
SingleLookupRequest req = new SingleLookupRequest();
req.setType(ThreePidMedium.Email.getId());
req.setThreePid(user1Email);
Optional<SingleLookupReply> lookup = store.find(req);
assertFalse(lookup.isPresent());
}
@Test
public void singleSuccessEmpty() {
ExecIdentityStore store = getStore("singleSuccessEmpty");
SingleLookupRequest req = new SingleLookupRequest();
req.setType(ThreePidMedium.Email.getId());
req.setThreePid(user1Email);
Optional<SingleLookupReply> lookup = store.find(req);
assertFalse(lookup.isPresent());
}
@Test
public void singleSuccessData() {
SingleLookupRequest req = new SingleLookupRequest();
req.setType(ThreePidMedium.Email.getId());
req.setThreePid(user1Email);
Optional<SingleLookupReply> lookup = getStore("singleSuccessData").find(req);
assertTrue(lookup.isPresent());
SingleLookupReply reply = lookup.get();
assertEquals(MatrixID.asAcceptable(user1Localpart, domain), reply.getMxid());
}
@Test(expected = InternalServerError.class)
public void singleSuccessEmptyFromInvalidOutput() {
SingleLookupRequest req = new SingleLookupRequest();
req.setType(ThreePidMedium.Email.getId());
req.setThreePid(user1Email);
getStore("singleSuccessEmptyFromInvalidOutput").find(req);
}
}

View File

@@ -0,0 +1,140 @@
/*
* 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.backend.exec;
import com.google.gson.JsonObject;
import io.kamax.matrix.MatrixID;
import io.kamax.matrix.ThreePid;
import io.kamax.matrix.ThreePidMedium;
import io.kamax.matrix._ThreePid;
import io.kamax.matrix.json.GsonUtil;
import io.kamax.mxisd.UserIdType;
import io.kamax.mxisd.config.ExecConfig;
import io.kamax.mxisd.profile.JsonProfileResult;
import org.junit.Test;
import java.util.List;
import java.util.Optional;
import static junit.framework.TestCase.assertTrue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
public class ExecProfileStoreTest extends ExecStoreTest {
private final String seo = "successEmptyOutput";
private final String sn = "successName";
private final String sst = "successSingleThreepid";
private final String smt = "successMultiThreepid";
private final String user1Msisdn = Long.toString(System.currentTimeMillis());
public ExecProfileStoreTest() {
executables.put(seo, () -> make(0, () -> "{}"));
executables.put(sn, () -> makeJson(0, () -> {
JsonObject profile = new JsonObject();
profile.addProperty("display_name", user1Name);
return GsonUtil.makeObj("profile", profile);
}));
executables.put(sst, () -> makeJson(0, () -> {
JsonProfileResult profile = new JsonProfileResult();
profile.addThreepid(new ThreePid(ThreePidMedium.Email.getId(), user1Email));
return GsonUtil.makeObj("profile", profile);
}));
executables.put(smt, () -> makeJson(0, () -> {
JsonProfileResult profile = new JsonProfileResult();
profile.addThreepid(new ThreePid(ThreePidMedium.Email.getId(), user1Email));
profile.addThreepid(new ThreePid(ThreePidMedium.PhoneNumber.getId(), user1Msisdn));
return GsonUtil.makeObj("profile", profile);
}));
}
private ExecConfig.Profile getCfg() {
ExecConfig.Profile cfg = new ExecConfig().compute().getProfile();
assertFalse(cfg.isEnabled());
cfg.setEnabled(true);
assertTrue(cfg.isEnabled());
cfg.getDisplayName().getOutput().setType(ExecStore.JsonType);
cfg.getThreePid().getOutput().setType(ExecStore.JsonType);
cfg.getRole().getOutput().setType(ExecStore.JsonType);
return cfg;
}
private ExecProfileStore getStore(ExecConfig.Profile cfg) {
ExecProfileStore store = new ExecProfileStore(cfg);
store.setExecutorSupplier(this::build);
assertTrue(store.isEnabled());
return store;
}
private ExecProfileStore getStore(String command) {
ExecConfig.Profile cfg = getCfg();
cfg.getDisplayName().setCommand(command);
cfg.getThreePid().setCommand(command);
cfg.getRole().setCommand(command);
return getStore(cfg);
}
@Test
public void getNameSuccessNoOutput() {
Optional<String> name = getStore(sno).getDisplayName(MatrixID.asAcceptable(user1Localpart, domain));
assertFalse(name.isPresent());
}
@Test
public void getNameSuccessEmptyOutput() {
Optional<String> name = getStore(seo).getDisplayName(MatrixID.asAcceptable(user1Localpart, domain));
assertFalse(name.isPresent());
}
@Test
public void getNameSuccess() {
Optional<String> name = getStore(sn).getDisplayName(user1Id);
assertTrue(name.isPresent());
assertEquals(user1Name, name.get());
}
@Test
public void getSingleThreePidSuccess() {
List<_ThreePid> tpids = getStore(sst).getThreepids(user1Id);
assertEquals(1, tpids.size());
_ThreePid tpid = tpids.get(0);
assertEquals(UserIdType.Email.getId(), tpid.getMedium());
assertEquals(user1Email, tpid.getAddress());
}
@Test
public void getMultiThreePidSuccess() {
List<_ThreePid> tpids = getStore(smt).getThreepids(user1Id);
assertEquals(2, tpids.size());
_ThreePid firstTpid = tpids.get(0);
assertEquals(ThreePidMedium.Email.getId(), firstTpid.getMedium());
assertEquals(user1Email, firstTpid.getAddress());
_ThreePid secondTpid = tpids.get(1);
assertEquals(ThreePidMedium.PhoneNumber.getId(), secondTpid.getMedium());
assertEquals(user1Msisdn, secondTpid.getAddress());
}
}

View File

@@ -0,0 +1,98 @@
/*
* 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.backend.exec;
import io.kamax.matrix.MatrixID;
import io.kamax.matrix._MatrixID;
import io.kamax.matrix.json.GsonUtil;
import io.kamax.mxisd.config.MatrixConfig;
import org.zeroturnaround.exec.ProcessExecutor;
import org.zeroturnaround.exec.ProcessResult;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import java.util.function.Supplier;
public class ExecStoreTest {
protected final String sno = "successNoOutput";
protected final String fno = "failureNoOutput";
protected final String uno = "unknownNoOutput";
protected final String domain = "domain.tld";
protected final String userLocalpart = "user";
protected final String user1Localpart = userLocalpart + "1";
protected final String user1Name = "User 1";
protected final String user2Localpart = userLocalpart + "2";
protected final String user2Name = "User 2";
protected final _MatrixID user1Id = MatrixID.asAcceptable(user1Localpart, domain);
protected final String user1Email = user1Localpart + "@" + domain;
protected Map<String, Supplier<ProcessResult>> executables = new HashMap<>();
public ExecStoreTest() {
executables.put(sno, () -> make(0, () -> ""));
executables.put(fno, () -> make(1, () -> ""));
executables.put(uno, () -> make(Integer.MAX_VALUE, () -> ""));
}
protected MatrixConfig getMatrixCfg() {
MatrixConfig mxCfg = new MatrixConfig();
mxCfg.setDomain(domain);
return mxCfg;
}
protected ProcessResult make(int exitCode, Supplier<String> supplier) {
return new ProcessResult(exitCode, null) {
@Override
public String outputUTF8() {
return supplier.get();
}
};
}
protected ProcessResult makeJson(int exitCode, Supplier<Object> supplier) {
return make(exitCode, () -> GsonUtil.get().toJson(supplier.get()));
}
protected ProcessExecutor build() {
return new ProcessExecutor() {
private Function<String, RuntimeException> notFound = command ->
new IllegalArgumentException("Command not found: " + command);
@Override
public ProcessResult execute() {
if (getCommand().size() == 0) throw new IllegalStateException();
String command = getCommand().get(0);
return executables.getOrDefault(command, () -> {
throw notFound.apply(command);
}).get();
}
};
}
}

View File

@@ -20,9 +20,11 @@
package io.kamax.mxisd.backend.exec.auth.input; package io.kamax.mxisd.backend.exec.auth.input;
import io.kamax.mxisd.backend.exec.ExecAuthStoreTest;
import java.util.Arrays; import java.util.Arrays;
public class ExecAuthArgsTest extends ExecAuthTest { public class ExecAuthArgsTest extends ExecAuthStoreTest {
@Override @Override
protected void setValidCommand() { protected void setValidCommand() {

View File

@@ -20,9 +20,11 @@
package io.kamax.mxisd.backend.exec.auth.input; package io.kamax.mxisd.backend.exec.auth.input;
import io.kamax.mxisd.backend.exec.ExecAuthStoreTest;
import java.util.HashMap; import java.util.HashMap;
public class ExecAuthEnvTest extends ExecAuthTest { public class ExecAuthEnvTest extends ExecAuthStoreTest {
private final String LocalpartEnv = "LOCALPART"; private final String LocalpartEnv = "LOCALPART";
private final String DomainEnv = "DOMAIN"; private final String DomainEnv = "DOMAIN";

View File

@@ -20,9 +20,10 @@
package io.kamax.mxisd.backend.exec.auth.input; package io.kamax.mxisd.backend.exec.auth.input;
import io.kamax.mxisd.backend.exec.ExecAuthStoreTest;
import io.kamax.mxisd.backend.exec.ExecStore; import io.kamax.mxisd.backend.exec.ExecStore;
public class ExecAuthInputMultilinesTest extends ExecAuthTest { public class ExecAuthInputMultilinesTest extends ExecAuthStoreTest {
@Override @Override
protected void setValidCommand() { protected void setValidCommand() {