Further progress on Exec Identity Store
This commit is contained in:
@@ -28,7 +28,9 @@ import io.kamax.mxisd.UserID;
|
||||
import io.kamax.mxisd.UserIdType;
|
||||
import io.kamax.mxisd.auth.provider.AuthenticatorProvider;
|
||||
import io.kamax.mxisd.config.ExecConfig;
|
||||
import io.kamax.mxisd.exception.ConfigurationException;
|
||||
import io.kamax.mxisd.exception.InternalServerError;
|
||||
import io.kamax.mxisd.util.TriFunction;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
@@ -42,6 +44,8 @@ import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Component
|
||||
@@ -49,11 +53,74 @@ public class ExecAuthStore extends ExecStore implements AuthenticatorProvider {
|
||||
|
||||
private final transient Logger log = LoggerFactory.getLogger(ExecAuthStore.class);
|
||||
|
||||
private Map<String, Supplier<String>> inputTemplates;
|
||||
private Map<String, BiConsumer<String, ExecAuthResult>> outputMapper;
|
||||
|
||||
private TriFunction<String, _MatrixID, String, String> inputMapper;
|
||||
|
||||
private ExecConfig.Auth cfg;
|
||||
|
||||
@Autowired
|
||||
public ExecAuthStore(ExecConfig cfg) {
|
||||
this.cfg = Objects.requireNonNull(cfg.getAuth());
|
||||
|
||||
inputTemplates = new HashMap<>();
|
||||
inputTemplates.put(JsonType, () -> {
|
||||
JsonObject json = new JsonObject();
|
||||
json.addProperty("localpart", cfg.getToken().getLocalpart());
|
||||
json.addProperty("domain", cfg.getToken().getDomain());
|
||||
json.addProperty("mxid", cfg.getToken().getMxid());
|
||||
json.addProperty("password", cfg.getToken().getPassword());
|
||||
return GsonUtil.get().toJson(json);
|
||||
});
|
||||
inputTemplates.put(MultilinesType, () -> cfg.getToken().getLocalpart() + System.lineSeparator() +
|
||||
cfg.getToken().getDomain() + System.lineSeparator() +
|
||||
cfg.getToken().getMxid() + System.lineSeparator() +
|
||||
cfg.getToken().getPassword() + System.lineSeparator()
|
||||
);
|
||||
|
||||
inputMapper = (input, uId, password) -> input.replace(cfg.getToken().getLocalpart(), uId.getLocalPart())
|
||||
.replace(cfg.getToken().getDomain(), uId.getDomain())
|
||||
.replace(cfg.getToken().getMxid(), uId.getId())
|
||||
.replace(cfg.getToken().getPassword(), password);
|
||||
|
||||
outputMapper = new HashMap<>();
|
||||
outputMapper.put(JsonType, (output, result) -> {
|
||||
JsonObject data = GsonUtil.getObj(GsonUtil.parseObj(output), "auth");
|
||||
GsonUtil.findPrimitive(data, "success")
|
||||
.map(JsonPrimitive::getAsBoolean)
|
||||
.ifPresent(result::setSuccess);
|
||||
GsonUtil.findObj(data, "profile")
|
||||
.flatMap(p -> GsonUtil.findString(p, "display_name"))
|
||||
.ifPresent(v -> result.getProfile().setDisplayName(v));
|
||||
});
|
||||
outputMapper.put(MultilinesType, (output, result) -> {
|
||||
String[] lines = output.split("\\R");
|
||||
if (lines.length > 2) {
|
||||
throw new InternalServerError("Exec auth command returned more than 2 lines (" + lines.length + ")");
|
||||
}
|
||||
|
||||
result.setSuccess(Optional.ofNullable(StringUtils.isEmpty(lines[0]) ? null : lines[0])
|
||||
.map(v -> StringUtils.equalsAnyIgnoreCase(v, "true", "1"))
|
||||
.orElse(result.isSuccess()));
|
||||
|
||||
if (lines.length == 2) {
|
||||
Optional.ofNullable(StringUtils.isEmpty(lines[1]) ? null : lines[1])
|
||||
.ifPresent(v -> result.getProfile().setDisplayName(v));
|
||||
}
|
||||
});
|
||||
|
||||
validateConfig();
|
||||
}
|
||||
|
||||
private void validateConfig() {
|
||||
if (StringUtils.isNotEmpty(cfg.getInput().getType()) && !inputTemplates.containsKey(cfg.getInput().getType())) {
|
||||
throw new ConfigurationException("Exec Auth input type is not valid: " + cfg.getInput().getType());
|
||||
}
|
||||
|
||||
if (StringUtils.isNotEmpty(cfg.getOutput().getType()) && !outputMapper.containsKey(cfg.getOutput().getType())) {
|
||||
throw new ConfigurationException("Exec Auth output type is not valid: " + cfg.getInput().getType());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -75,31 +142,17 @@ public class ExecAuthStore extends ExecStore implements AuthenticatorProvider {
|
||||
|
||||
List<String> args = new ArrayList<>();
|
||||
args.add(cfg.getCommand());
|
||||
args.addAll(cfg.getArgs().stream().map(arg -> arg
|
||||
.replace(cfg.getToken().getLocalpart(), uId.getLocalPart())
|
||||
.replace(cfg.getToken().getDomain(), uId.getDomain())
|
||||
.replace(cfg.getToken().getMxid(), uId.getId())
|
||||
.replace(cfg.getToken().getPassword(), password)
|
||||
).collect(Collectors.toList()));
|
||||
args.addAll(cfg.getArgs().stream().map(arg -> inputMapper.apply(arg, uId, password)).collect(Collectors.toList()));
|
||||
psExec.command(args);
|
||||
|
||||
psExec.environment(new HashMap<>(cfg.getEnv()).entrySet().stream().peek(e -> {
|
||||
e.setValue(e.getValue().replace(cfg.getToken().getLocalpart(), uId.getLocalPart()));
|
||||
e.setValue(e.getValue().replace(cfg.getToken().getDomain(), uId.getDomain()));
|
||||
e.setValue(e.getValue().replace(cfg.getToken().getMxid(), uId.getId()));
|
||||
e.setValue(e.getValue().replace(cfg.getToken().getPassword(), password));
|
||||
}).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)));
|
||||
psExec.environment(new HashMap<>(cfg.getEnv()).entrySet().stream()
|
||||
.peek(e -> e.setValue(inputMapper.apply(e.getValue(), uId, password)))
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)));
|
||||
|
||||
if (StringUtils.isNotBlank(cfg.getInput())) {
|
||||
if (StringUtils.equals("json", cfg.getInput())) {
|
||||
JsonObject input = new JsonObject();
|
||||
input.addProperty("localpart", uId.getLocalPart());
|
||||
input.addProperty("mxid", uId.getId());
|
||||
input.addProperty("password", password);
|
||||
psExec.redirectInput(IOUtils.toInputStream(GsonUtil.get().toJson(input), StandardCharsets.UTF_8));
|
||||
} else {
|
||||
throw new InternalServerError(cfg.getInput() + " is not a valid executable input format");
|
||||
}
|
||||
if (StringUtils.isNotBlank(cfg.getInput().getType())) {
|
||||
String template = cfg.getInput().getTemplate().orElseGet(inputTemplates.get(cfg.getInput().getType()));
|
||||
String input = inputMapper.apply(template, uId, password);
|
||||
psExec.redirectInput(IOUtils.toInputStream(input, StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -107,28 +160,23 @@ public class ExecAuthStore extends ExecStore implements AuthenticatorProvider {
|
||||
ProcessResult psResult = psExec.execute();
|
||||
result.setExitStatus(psResult.getExitValue());
|
||||
String output = psResult.outputUTF8();
|
||||
log.debug("Command output:{}{}", System.lineSeparator(), output);
|
||||
|
||||
log.info("Exit status: {}", result.getExitStatus());
|
||||
if (cfg.getExit().getSuccess().contains(result.getExitStatus())) {
|
||||
result.setSuccess(true);
|
||||
if (result.isSuccess()) {
|
||||
if (StringUtils.equals("json", cfg.getOutput())) {
|
||||
JsonObject data = GsonUtil.parseObj(output);
|
||||
GsonUtil.findPrimitive(data, "success")
|
||||
.map(JsonPrimitive::getAsBoolean)
|
||||
.ifPresent(result::setSuccess);
|
||||
GsonUtil.findObj(data, "profile")
|
||||
.flatMap(p -> GsonUtil.findString(p, "display_name"))
|
||||
.ifPresent(v -> result.getProfile().setDisplayName(v));
|
||||
} else {
|
||||
log.debug("Command output:{}{}", "\n", output);
|
||||
if (result.isSuccess() && StringUtils.isNotEmpty(output)) {
|
||||
outputMapper.get(cfg.getOutput().getType()).accept(output, result);
|
||||
} else {
|
||||
if (StringUtils.isNotEmpty(output)) {
|
||||
log.info("Exec auth failed with output:{}{}", System.lineSeparator(), output);
|
||||
}
|
||||
}
|
||||
} else if (cfg.getExit().getFailure().contains(result.getExitStatus())) {
|
||||
log.debug("{} stdout:{}{}", cfg.getCommand(), "\n", output);
|
||||
log.debug("{} stdout:{}{}", cfg.getCommand(), System.lineSeparator(), output);
|
||||
result.setSuccess(false);
|
||||
} else {
|
||||
log.error("{} stdout:{}{}", cfg.getCommand(), "\n", output);
|
||||
log.error("{} stdout:{}{}", cfg.getCommand(), System.lineSeparator(), output);
|
||||
throw new InternalServerError("Exec auth command returned with unexpected exit status");
|
||||
}
|
||||
|
||||
|
@@ -20,17 +20,26 @@
|
||||
|
||||
package io.kamax.mxisd.backend.exec;
|
||||
|
||||
import io.kamax.mxisd.config.ExecConfig;
|
||||
import io.kamax.mxisd.controller.directory.v1.io.UserDirectorySearchResult;
|
||||
import io.kamax.mxisd.directory.IDirectoryProvider;
|
||||
import io.kamax.mxisd.exception.NotImplementedException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class ExecDirectoryStore extends ExecStore implements IDirectoryProvider {
|
||||
|
||||
private ExecConfig.Directory cfg;
|
||||
|
||||
@Autowired
|
||||
public ExecDirectoryStore(ExecConfig cfg) {
|
||||
this.cfg = cfg.getDirectory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return false;
|
||||
return cfg.isEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -20,22 +20,129 @@
|
||||
|
||||
package io.kamax.mxisd.backend.exec;
|
||||
|
||||
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.config.ExecConfig;
|
||||
import io.kamax.mxisd.config.MatrixConfig;
|
||||
import io.kamax.mxisd.exception.ConfigurationException;
|
||||
import io.kamax.mxisd.exception.InternalServerError;
|
||||
import io.kamax.mxisd.exception.NotImplementedException;
|
||||
import io.kamax.mxisd.lookup.SingleLookupReply;
|
||||
import io.kamax.mxisd.lookup.SingleLookupRequest;
|
||||
import io.kamax.mxisd.lookup.ThreePidMapping;
|
||||
import io.kamax.mxisd.lookup.provider.IThreePidProvider;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.zeroturnaround.exec.ProcessExecutor;
|
||||
import org.zeroturnaround.exec.ProcessResult;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Component
|
||||
public class ExecIdentityStore extends ExecStore implements IThreePidProvider {
|
||||
|
||||
private final Logger log = LoggerFactory.getLogger(ExecIdentityStore.class);
|
||||
|
||||
private final ExecConfig.Identity cfg;
|
||||
private final MatrixConfig mxCfg;
|
||||
|
||||
private BiFunction<String, SingleLookupRequest, String> singleInputMap;
|
||||
private Map<String, Supplier<String>> singleInputTemplates;
|
||||
private Map<String, Function<String, Optional<_MatrixID>>> singleOutputMap;
|
||||
|
||||
@Autowired
|
||||
public ExecIdentityStore(ExecConfig cfg, MatrixConfig mxCfg) {
|
||||
this.cfg = cfg.getIdentity();
|
||||
this.mxCfg = mxCfg;
|
||||
|
||||
singleInputMap = (v, request) -> v.replace(cfg.getToken().getMedium(), request.getType())
|
||||
.replace(cfg.getToken().getAddress(), request.getThreePid());
|
||||
|
||||
singleInputTemplates = new HashMap<>();
|
||||
singleInputTemplates.put(JsonType, () -> {
|
||||
JsonObject json = new JsonObject();
|
||||
json.addProperty("medium", cfg.getToken().getMedium());
|
||||
json.addProperty("address", cfg.getToken().getAddress());
|
||||
return GsonUtil.get().toJson(json);
|
||||
});
|
||||
singleInputTemplates.put(MultilinesType, () -> cfg.getToken().getMedium()
|
||||
+ System.lineSeparator()
|
||||
+ cfg.getToken().getAddress()
|
||||
);
|
||||
|
||||
singleOutputMap = new HashMap<>();
|
||||
singleOutputMap.put(JsonType, output -> {
|
||||
if (StringUtils.isBlank(output)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
return GsonUtil.findObj(GsonUtil.parseObj(output), "lookup").map(lookup -> {
|
||||
String type = GsonUtil.getStringOrThrow(lookup, "type");
|
||||
String value = GsonUtil.getStringOrThrow(lookup, "value");
|
||||
if (StringUtils.equals(type, "uid")) {
|
||||
return MatrixID.asAcceptable(value, mxCfg.getDomain());
|
||||
}
|
||||
|
||||
if (StringUtils.equals(type, "mxid")) {
|
||||
return MatrixID.asAcceptable(value);
|
||||
}
|
||||
|
||||
throw new InternalServerError("Invalid user type: " + type);
|
||||
});
|
||||
});
|
||||
singleOutputMap.put(MultilinesType, output -> {
|
||||
String[] lines = output.split("\\R");
|
||||
if (lines.length > 2) {
|
||||
throw new InternalServerError("Exec auth command returned more than 2 lines (" + lines.length + ")");
|
||||
}
|
||||
|
||||
if (lines.length == 1 && StringUtils.isBlank(lines[0])) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
String type = StringUtils.trimToEmpty(lines.length == 1 ? "uid" : lines[0]);
|
||||
String value = StringUtils.trimToEmpty(lines.length == 2 ? lines[1] : lines[0]);
|
||||
|
||||
if (StringUtils.equals(type, "uid")) {
|
||||
return Optional.of(MatrixID.asAcceptable(value, mxCfg.getDomain()));
|
||||
}
|
||||
|
||||
if (StringUtils.equals(type, "mxid")) {
|
||||
return Optional.of(MatrixID.asAcceptable(value));
|
||||
}
|
||||
|
||||
throw new InternalServerError("Invalid user type: " + type);
|
||||
});
|
||||
|
||||
validateConfig();
|
||||
}
|
||||
|
||||
private void validateConfig() {
|
||||
if (StringUtils.isNotEmpty(cfg.getInput().getType()) && !singleInputTemplates.containsKey(cfg.getInput().getType())) {
|
||||
throw new ConfigurationException("Exec Identity Single Lookup: input type is not valid: " + cfg.getInput().getType());
|
||||
}
|
||||
|
||||
if (StringUtils.isNotEmpty(cfg.getOutput().getType()) && !singleOutputMap.containsKey(cfg.getOutput().getType())) {
|
||||
throw new ConfigurationException("Exec Auth output type is not valid: " + cfg.getInput().getType());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return false;
|
||||
return cfg.isEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -45,12 +152,53 @@ public class ExecIdentityStore extends ExecStore implements IThreePidProvider {
|
||||
|
||||
@Override
|
||||
public int getPriority() {
|
||||
return 0;
|
||||
return cfg.getPriority();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<SingleLookupReply> find(SingleLookupRequest request) {
|
||||
throw new NotImplementedException(this.getClass().getName());
|
||||
ProcessExecutor psExec = new ProcessExecutor().readOutput(true);
|
||||
|
||||
List<String> args = new ArrayList<>();
|
||||
args.add(cfg.getCommand());
|
||||
args.addAll(cfg.getArgs().stream().map(arg -> singleInputMap.apply(arg, request)).collect(Collectors.toList()));
|
||||
psExec.command(args);
|
||||
|
||||
psExec.environment(new HashMap<>(cfg.getEnv()).entrySet().stream()
|
||||
.peek(e -> e.setValue(singleInputMap.apply(e.getValue(), request)))
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)));
|
||||
|
||||
if (StringUtils.isNotBlank(cfg.getInput().getType())) {
|
||||
String template = cfg.getInput().getTemplate().orElseGet(singleInputTemplates.get(cfg.getInput().getType()));
|
||||
String input = singleInputMap.apply(template, request);
|
||||
psExec.redirectInput(IOUtils.toInputStream(input, StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
try {
|
||||
log.info("Executing {}", cfg.getCommand());
|
||||
ProcessResult psResult = psExec.execute();
|
||||
String output = psResult.outputUTF8();
|
||||
log.debug("Command output:{}{}", System.lineSeparator(), output);
|
||||
|
||||
log.info("Exit status: {}", psResult.getExitValue());
|
||||
if (cfg.getExit().getSuccess().contains(psResult.getExitValue())) {
|
||||
if (StringUtils.isBlank(output)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
return singleOutputMap.get(cfg.getOutput().getType())
|
||||
.apply(output)
|
||||
.map(mxId -> new SingleLookupReply(request, mxId));
|
||||
} else if (cfg.getExit().getFailure().contains(psResult.getExitValue())) {
|
||||
log.debug("{} stdout:{}{}", cfg.getCommand(), System.lineSeparator(), output);
|
||||
return Optional.empty();
|
||||
} else {
|
||||
log.error("{} stdout:{}{}", cfg.getCommand(), System.lineSeparator(), output);
|
||||
throw new InternalServerError("Exec auth command returned with unexpected exit status");
|
||||
}
|
||||
} catch (IOException | InterruptedException | TimeoutException e) {
|
||||
throw new InternalServerError(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -22,8 +22,10 @@ package io.kamax.mxisd.backend.exec;
|
||||
|
||||
import io.kamax.matrix._MatrixID;
|
||||
import io.kamax.matrix._ThreePid;
|
||||
import io.kamax.mxisd.config.ExecConfig;
|
||||
import io.kamax.mxisd.exception.NotImplementedException;
|
||||
import io.kamax.mxisd.profile.ProfileProvider;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
@@ -32,9 +34,16 @@ import java.util.Optional;
|
||||
@Component
|
||||
public class ExecProfileStore extends ExecStore implements ProfileProvider {
|
||||
|
||||
private ExecConfig.Profile cfg;
|
||||
|
||||
@Autowired
|
||||
public ExecProfileStore(ExecConfig cfg) {
|
||||
this.cfg = cfg.getProfile();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return false;
|
||||
return cfg.isEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -22,6 +22,7 @@ package io.kamax.mxisd.backend.exec;
|
||||
|
||||
public abstract class ExecStore {
|
||||
|
||||
// no-op
|
||||
public static final String JsonType = "json";
|
||||
public static final String MultilinesType = "multilines";
|
||||
|
||||
}
|
||||
|
@@ -31,6 +31,29 @@ import java.util.*;
|
||||
@ConfigurationProperties("exec")
|
||||
public class ExecConfig {
|
||||
|
||||
public class IO {
|
||||
|
||||
private String type;
|
||||
private String template;
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public Optional<String> getTemplate() {
|
||||
return Optional.ofNullable(template);
|
||||
}
|
||||
|
||||
public void setTemplate(String template) {
|
||||
this.template = template;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class Exit {
|
||||
|
||||
private List<Integer> success = Collections.singletonList(0);
|
||||
@@ -101,6 +124,8 @@ public class ExecConfig {
|
||||
private String domain = "{domain}";
|
||||
private String mxid = "{mxid}";
|
||||
private String password = "{password}";
|
||||
private String medium = "{medium}";
|
||||
private String address = "{address}";
|
||||
|
||||
public String getLocalpart() {
|
||||
return localpart;
|
||||
@@ -134,6 +159,22 @@ public class ExecConfig {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getMedium() {
|
||||
return medium;
|
||||
}
|
||||
|
||||
public void setMedium(String medium) {
|
||||
this.medium = medium;
|
||||
}
|
||||
|
||||
public String getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public void setAddress(String address) {
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class Process {
|
||||
@@ -143,10 +184,10 @@ public class ExecConfig {
|
||||
|
||||
private List<String> args = new ArrayList<>();
|
||||
private Map<String, String> env = new HashMap<>();
|
||||
private String input;
|
||||
private IO input = new IO();
|
||||
|
||||
private Exit exit = new Exit();
|
||||
private String output;
|
||||
private IO output = new IO();
|
||||
|
||||
public TokenOverride getToken() {
|
||||
return token;
|
||||
@@ -184,11 +225,11 @@ public class ExecConfig {
|
||||
this.env.put(key, value);
|
||||
}
|
||||
|
||||
public String getInput() {
|
||||
public IO getInput() {
|
||||
return input;
|
||||
}
|
||||
|
||||
public void setInput(String input) {
|
||||
public void setInput(IO input) {
|
||||
this.input = input;
|
||||
}
|
||||
|
||||
@@ -200,11 +241,11 @@ public class ExecConfig {
|
||||
this.exit = exit;
|
||||
}
|
||||
|
||||
public String getOutput() {
|
||||
public IO getOutput() {
|
||||
return output;
|
||||
}
|
||||
|
||||
public void setOutput(String output) {
|
||||
public void setOutput(IO output) {
|
||||
this.output = output;
|
||||
}
|
||||
|
||||
@@ -224,9 +265,33 @@ public class ExecConfig {
|
||||
|
||||
}
|
||||
|
||||
public class Directory extends Process {
|
||||
public class Directory {
|
||||
|
||||
public class Search {
|
||||
|
||||
private Process byName = new Process();
|
||||
private Process byThreepid = new Process();
|
||||
|
||||
public Process getByName() {
|
||||
return byName;
|
||||
}
|
||||
|
||||
public void setByName(Process byName) {
|
||||
this.byName = byName;
|
||||
}
|
||||
|
||||
public Process getByThreepid() {
|
||||
return byThreepid;
|
||||
}
|
||||
|
||||
public void setByThreepid(Process byThreepid) {
|
||||
this.byThreepid = byThreepid;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private Boolean enabled;
|
||||
private Search search = new Search();
|
||||
|
||||
public Boolean isEnabled() {
|
||||
return enabled;
|
||||
@@ -236,11 +301,20 @@ public class ExecConfig {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public Search getSearch() {
|
||||
return search;
|
||||
}
|
||||
|
||||
public void setSearch(Search search) {
|
||||
this.search = search;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class Identity extends Process {
|
||||
|
||||
private Boolean enabled;
|
||||
private int priority;
|
||||
|
||||
public Boolean isEnabled() {
|
||||
return enabled;
|
||||
@@ -250,6 +324,14 @@ public class ExecConfig {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public int getPriority() {
|
||||
return priority;
|
||||
}
|
||||
|
||||
public void setPriority(int priority) {
|
||||
this.priority = priority;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class Profile extends Process {
|
||||
|
28
src/main/java/io/kamax/mxisd/util/TriFunction.java
Normal file
28
src/main/java/io/kamax/mxisd/util/TriFunction.java
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface TriFunction<T, U, V, R> {
|
||||
|
||||
R apply(T t, U u, V v);
|
||||
|
||||
}
|
@@ -18,15 +18,15 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.kamax.mxisd.backend.exec;
|
||||
package io.kamax.mxisd.backend.exec.input;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class ExecAuthStoreArgsTest extends ExecAuthStoreTest {
|
||||
public class ArgsTest extends InputTest {
|
||||
|
||||
@Override
|
||||
protected void setValidCommand() {
|
||||
cfg.getAuth().setCommand("src/test/resources/store/exec/authArgsTest.sh");
|
||||
cfg.getAuth().setCommand("src/test/resources/store/exec/input/argsTest.sh");
|
||||
}
|
||||
|
||||
@Override
|
@@ -18,9 +18,11 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.kamax.mxisd.backend.exec;
|
||||
package io.kamax.mxisd.backend.exec.input;
|
||||
|
||||
public class ExecAuthStoreEnvTest extends ExecAuthStoreTest {
|
||||
import java.util.HashMap;
|
||||
|
||||
public class EnvTest extends InputTest {
|
||||
|
||||
private final String LocalpartEnv = "LOCALPART";
|
||||
private final String DomainEnv = "DOMAIN";
|
||||
@@ -28,11 +30,12 @@ public class ExecAuthStoreEnvTest extends ExecAuthStoreTest {
|
||||
|
||||
@Override
|
||||
protected void setValidCommand() {
|
||||
cfg.getAuth().setCommand("src/test/resources/store/exec/authEnvTest.sh");
|
||||
cfg.getAuth().setCommand("src/test/resources/store/exec/input/envTest.sh");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setValidEnv() {
|
||||
cfg.getAuth().setEnv(new HashMap<>());
|
||||
cfg.getAuth().addEnv(LocalpartEnv, LocalpartToken);
|
||||
cfg.getAuth().addEnv(DomainEnv, DomainToken);
|
||||
cfg.getAuth().addEnv(MxidEnv, MxidToken);
|
@@ -18,13 +18,16 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.kamax.mxisd.backend.exec;
|
||||
package io.kamax.mxisd.backend.exec.input;
|
||||
|
||||
import io.kamax.matrix.MatrixID;
|
||||
import io.kamax.matrix._MatrixID;
|
||||
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 org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
@@ -32,7 +35,7 @@ import java.util.Collections;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public abstract class ExecAuthStoreTest {
|
||||
public abstract class InputTest {
|
||||
|
||||
protected final ExecConfig cfg;
|
||||
protected final ExecAuthStore p;
|
||||
@@ -61,20 +64,23 @@ public abstract class ExecAuthStoreTest {
|
||||
// no-op
|
||||
}
|
||||
|
||||
protected void setValidInput() {
|
||||
// no-op
|
||||
}
|
||||
|
||||
protected void setValidExit() {
|
||||
cfg.getAuth().getExit().setSuccess(Collections.singletonList(0));
|
||||
cfg.getAuth().getExit().setFailure(Arrays.asList(1, 10, 11, 12, 20, 21, 22));
|
||||
}
|
||||
|
||||
protected void setValidConfig() {
|
||||
@Before
|
||||
public void setValidConfig() {
|
||||
setValidCommand();
|
||||
setValidEnv();
|
||||
setValidArgs();
|
||||
setValidInput();
|
||||
setValidExit();
|
||||
}
|
||||
|
||||
public ExecAuthStoreTest() {
|
||||
cfg = new ExecConfig();
|
||||
cfg.getAuth().addEnv("WITH_LOCALPART", "1");
|
||||
cfg.getAuth().addEnv("REQ_LOCALPART", uId.getLocalPart());
|
||||
cfg.getAuth().addEnv("WITH_DOMAIN", "1");
|
||||
@@ -82,9 +88,10 @@ public abstract class ExecAuthStoreTest {
|
||||
cfg.getAuth().addEnv("WITH_MXID", "1");
|
||||
cfg.getAuth().addEnv("REQ_MXID", uId.getId());
|
||||
cfg.getAuth().addEnv("REQ_PASS", requiredPass);
|
||||
}
|
||||
|
||||
setValidConfig();
|
||||
|
||||
public InputTest() {
|
||||
cfg = new ExecConfig();
|
||||
p = new ExecAuthStore(cfg);
|
||||
}
|
||||
|
||||
@@ -119,7 +126,7 @@ public abstract class ExecAuthStoreTest {
|
||||
protected abstract void setEmptyLocalpartConfig();
|
||||
|
||||
@Test
|
||||
public void doEmptyLocalpartConfig() {
|
||||
public void emptyLocalpartConfig() {
|
||||
setEmptyLocalpartConfig();
|
||||
|
||||
ExecAuthResult res = p.authenticate(uId, requiredPass);
|
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* 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.input;
|
||||
|
||||
import io.kamax.mxisd.backend.exec.ExecStore;
|
||||
|
||||
public class MultilinesTest extends InputTest {
|
||||
|
||||
@Override
|
||||
protected void setValidCommand() {
|
||||
cfg.getAuth().setCommand("src/test/resources/store/exec/input/multilinesTest.sh");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setValidInput() {
|
||||
cfg.getAuth().getInput().setType(ExecStore.MultilinesType);
|
||||
cfg.getAuth().getInput().setTemplate(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setEmptyLocalpartConfig() {
|
||||
cfg.getAuth().getInput().setTemplate("" + System.lineSeparator()
|
||||
+ DomainToken + System.lineSeparator()
|
||||
+ MxidToken + System.lineSeparator()
|
||||
+ PassToken + System.lineSeparator()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWrongLocalpartConfig() {
|
||||
cfg.getAuth().getInput().setTemplate(LocalpartInvalid + System.lineSeparator()
|
||||
+ DomainToken + System.lineSeparator()
|
||||
+ MxidToken + System.lineSeparator()
|
||||
+ PassToken + System.lineSeparator()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setEmptyDomainConfig() {
|
||||
cfg.getAuth().getInput().setTemplate(LocalpartToken + System.lineSeparator()
|
||||
+ "" + System.lineSeparator()
|
||||
+ MxidToken + System.lineSeparator()
|
||||
+ PassToken + System.lineSeparator()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWrongDomainConfig() {
|
||||
cfg.getAuth().getInput().setTemplate(LocalpartToken + System.lineSeparator()
|
||||
+ DomainInvalid + System.lineSeparator()
|
||||
+ MxidToken + System.lineSeparator()
|
||||
+ PassToken + System.lineSeparator()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setEmptyMxidConfig() {
|
||||
cfg.getAuth().getInput().setTemplate(LocalpartToken + System.lineSeparator()
|
||||
+ DomainToken + System.lineSeparator()
|
||||
+ "" + System.lineSeparator()
|
||||
+ PassToken + System.lineSeparator()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWrongMxidConfig() {
|
||||
cfg.getAuth().getInput().setTemplate(LocalpartToken + System.lineSeparator()
|
||||
+ DomainToken + System.lineSeparator()
|
||||
+ MxidInvalid + System.lineSeparator()
|
||||
+ PassToken + System.lineSeparator()
|
||||
);
|
||||
}
|
||||
|
||||
}
|
42
src/test/resources/store/exec/input/multilinesTest.sh
Executable file
42
src/test/resources/store/exec/input/multilinesTest.sh
Executable file
@@ -0,0 +1,42 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# 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/>.
|
||||
|
||||
if [ -n "$WITH_LOCALPART" ]; then
|
||||
read LOCALPART
|
||||
[ -n "$LOCALPART" ] || exit 10
|
||||
[ "$LOCALPART" = "$REQ_LOCALPART" ] || exit 20
|
||||
fi
|
||||
|
||||
if [ -n "$WITH_DOMAIN" ]; then
|
||||
read DOMAIN
|
||||
[ -n "$DOMAIN" ] || exit 11
|
||||
[ "$DOMAIN" = "$REQ_DOMAIN" ] || exit 21
|
||||
fi
|
||||
|
||||
if [ -n "$WITH_MXID" ]; then
|
||||
read MXID
|
||||
[ -n "$MXID" ] || exit 12
|
||||
[ "$MXID" = "$REQ_MXID" ] || exit 22
|
||||
fi
|
||||
|
||||
read PASS
|
||||
[ "$PASS" = "$REQ_PASS" ] || exit 1
|
||||
|
||||
exit 0
|
Reference in New Issue
Block a user