Add initial experimental support for #58

- Skeleton for the whole identity store
- Support Authentication
This commit is contained in:
Max Dor
2018-10-20 08:08:14 +02:00
parent cb02f62b9d
commit 99d793b5ed
18 changed files with 1219 additions and 13 deletions

View File

@@ -126,6 +126,9 @@ dependencies {
// SendGrid SDK to send emails from GCE
compile 'com.sendgrid:sendgrid-java:2.2.2'
// ZT-Exec for exec identity store
compile 'org.zeroturnaround:zt-exec:1.10'
testCompile 'junit:junit:4.12'
testCompile 'com.github.tomakehurst:wiremock:2.8.0'
}

View File

@@ -1,7 +1,8 @@
# Identity Stores
- [Synapse](synapse.md)
- [LDAP-based](ldap.md)
- [SQL Databases](sql.md)
- [Website / Web service / Web app](rest.md)
- [Google Firebase](firebase.md)
- [Wordpress](wordpress.md)
- [Synapse](synapse.md) - Turn your SynapseDB into a self-contained Identity store
- [LDAP-based](ldap.md) - Any LDAP-based product like Active Directory, Samba, NetIQ, OpenLDAP
- [SQL Databases](sql.md) - Most common databases like MariaDB, MySQL, PostgreSQL, SQLite
- [Website / Web service / Web app](rest.md) - Arbitrary REST endpoints
- [Executables](exec.md) - Run arbitrary executables with configurable stdin, arguments, environment and stdout
- [Wordpress](wordpress.md) - Connect your Wordpress-powered website DB
- [Google Firebase](firebase.md) - Use your Firebase users (with experimental SSO support!)

60
docs/stores/exec.md Normal file
View File

@@ -0,0 +1,60 @@
# Exec Identity Store
This Identity Store lets you run arbitrary commands to handle the various requests in each support feature.
This is the most versatile Identity store of mxisd, allowing you to connect any kind of logic in any language/scripting.
## Features
| Name | Supported? |
|----------------|---------------|
| Authentication | Yes |
| Directory | *In Progress* |
| Identity | *In Progress* |
| Profile | *In Progress* |
## Overview
Each request can be mapping to a fully customizable command configuration.
The various parameters can be provided via any combination of:
- Standard Input
- Command line arguments
- Environment variables
Each of those supports a set of customizable token which will be replaced prior to running the command, allowing to
provide the input values in any number of ways.
Success and data will be provided via [Exit status](https://en.wikipedia.org/wiki/Exit_status) and Standard Output, both
supporting a set of options.
## Configuration
```yaml
exec.enabled: <boolean>
```
Enable/disable the Identity store at a global/default level. Each feature can still be enabled/disabled specifically.
*TBC*
## Use-case examples
```yaml
exec.enabled: true
exec.auth.command: '/path/to/auth/executable'
exec.auth.args: ['-u', '{localpart}']
exec.auth.env:
PASSWORD: '{password}'
MATRIX_DOMAIN: '{domain}'
MATRIX_USER_ID: '{mxid}'
```
This will run `/path/to/auth/executable` with:
- The extracted Matrix User ID `localpart` provided as the second command line argument, the first one being `-u`
- The password, the extract Matrix `domain` and the full User ID as arbitrary environment variables, respectively `PASSWORD`, `MATRIX_DOMAIN` and `MATRIX_USER_ID`
```yaml
## Few more available config items
#
# exec.token.domain: '{matrixDomain}' # This sets the default replacement token for the Matrix Domain of the User ID, across all features.
# exec.auth.token.domain: '{matrixDomainForAuth}' # We can also set another token specific to a feature.
# exec.auth.input: 'json' # This is not supported yet.
# exec.auth.exit.success: [0] # Exit status that will consider the request successful. This is already the default.
# exec.auth.exit.failure: [1,2,3] # Exist status that will consider the request failed. Anything else than success or failure statuses will throw an exception.
# exec.auth.output: 'json' # Required if stdout should be read on success. This uses the same output as the REST Identity store for Auth.
```
*TBC*

View File

@@ -1,8 +1,8 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2017 Maxime Dor
* Copyright (C) 2017 Kamax Sarl
*
* https://max.kamax.io/
* 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
@@ -30,6 +30,10 @@ public class UserID {
// stub for (de)serialization
}
public UserID(UserIdType type, String value) {
this(type.getId(), value);
}
public UserID(String type, String value) {
this.type = type;
this.value = value;

View File

@@ -1,8 +1,8 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2017 Maxime Dor
* Copyright (C) 2017 Kamax Sarl
*
* https://max.kamax.io/
* 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
@@ -59,9 +59,10 @@ public class AuthManager {
continue;
}
log.info("Attempting authentication with store {}", provider.getClass().getSimpleName());
BackendAuthResult result = provider.authenticate(mxid, password);
if (result.isSuccess()) {
String mxId;
if (UserIdType.Localpart.is(result.getId().getType())) {
mxId = MatrixID.from(result.getId().getValue(), mxCfg.getDomain()).acceptable().getId();

View File

@@ -1,8 +1,8 @@
/*
* mxisd - Matrix Identity Server Daemon
* Copyright (C) 2017 Maxime Dor
* Copyright (C) 2017 Kamax Sarl
*
* https://max.kamax.io/
* 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
@@ -38,6 +38,10 @@ public class BackendAuthResult {
return displayName;
}
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
public Set<ThreePid> getThreePids() {
return threePids;
}
@@ -73,6 +77,10 @@ public class BackendAuthResult {
private UserID id;
private BackendAuthProfile profile = new BackendAuthProfile();
public void setSuccess(boolean success) {
this.success = success;
}
public Boolean isSuccess() {
return success;
}
@@ -81,6 +89,10 @@ public class BackendAuthResult {
return id;
}
public void setId(UserID id) {
this.id = id;
}
public BackendAuthProfile getProfile() {
return profile;
}

View File

@@ -0,0 +1,37 @@
/*
* 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.mxisd.auth.provider.BackendAuthResult;
public class ExecAuthResult extends BackendAuthResult {
private int exitStatus;
public int getExitStatus() {
return exitStatus;
}
public void setExitStatus(int exitStatus) {
this.exitStatus = exitStatus;
}
}

View File

@@ -0,0 +1,141 @@
/*
* 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 com.google.gson.JsonPrimitive;
import io.kamax.matrix._MatrixID;
import io.kamax.matrix.json.GsonUtil;
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.InternalServerError;
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.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.TimeoutException;
import java.util.stream.Collectors;
@Component
public class ExecAuthStore extends ExecStore implements AuthenticatorProvider {
private final transient Logger log = LoggerFactory.getLogger(ExecAuthStore.class);
private ExecConfig.Auth cfg;
@Autowired
public ExecAuthStore(ExecConfig cfg) {
this.cfg = Objects.requireNonNull(cfg.getAuth());
}
@Override
public boolean isEnabled() {
return cfg.isEnabled();
}
@Override
public ExecAuthResult authenticate(_MatrixID uId, String password) {
Objects.requireNonNull(uId);
Objects.requireNonNull(password);
log.info("Performing authentication for {}", uId.getId());
ExecAuthResult result = new ExecAuthResult();
result.setId(new UserID(UserIdType.Localpart, uId.getLocalPart()));
ProcessExecutor psExec = new ProcessExecutor().readOutput(true);
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()));
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)));
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");
}
}
try {
log.info("Executing {}", cfg.getCommand());
ProcessResult psResult = psExec.execute();
result.setExitStatus(psResult.getExitValue());
String output = psResult.outputUTF8();
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);
}
}
} else if (cfg.getExit().getFailure().contains(result.getExitStatus())) {
log.debug("{} stdout:{}{}", cfg.getCommand(), "\n", output);
result.setSuccess(false);
} else {
log.error("{} stdout:{}{}", cfg.getCommand(), "\n", output);
throw new InternalServerError("Exec auth command returned with unexpected exit status");
}
return result;
} catch (IOException | InterruptedException | TimeoutException e) {
throw new InternalServerError(e);
}
}
}

View File

@@ -0,0 +1,46 @@
/*
* 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.mxisd.controller.directory.v1.io.UserDirectorySearchResult;
import io.kamax.mxisd.directory.IDirectoryProvider;
import io.kamax.mxisd.exception.NotImplementedException;
import org.springframework.stereotype.Component;
@Component
public class ExecDirectoryStore extends ExecStore implements IDirectoryProvider {
@Override
public boolean isEnabled() {
return false;
}
@Override
public UserDirectorySearchResult searchByDisplayName(String query) {
throw new NotImplementedException(this.getClass().getName());
}
@Override
public UserDirectorySearchResult searchBy3pid(String query) {
throw new NotImplementedException(this.getClass().getName());
}
}

View File

@@ -0,0 +1,61 @@
/*
* 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.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.springframework.stereotype.Component;
import java.util.List;
import java.util.Optional;
@Component
public class ExecIdentityStore extends ExecStore implements IThreePidProvider {
@Override
public boolean isEnabled() {
return false;
}
@Override
public boolean isLocal() {
return true;
}
@Override
public int getPriority() {
return 0;
}
@Override
public Optional<SingleLookupReply> find(SingleLookupRequest request) {
throw new NotImplementedException(this.getClass().getName());
}
@Override
public List<ThreePidMapping> populate(List<ThreePidMapping> mappings) {
throw new NotImplementedException(this.getClass().getName());
}
}

View File

@@ -0,0 +1,55 @@
/*
* 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._ThreePid;
import io.kamax.mxisd.exception.NotImplementedException;
import io.kamax.mxisd.profile.ProfileProvider;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Optional;
@Component
public class ExecProfileStore extends ExecStore implements ProfileProvider {
@Override
public boolean isEnabled() {
return false;
}
@Override
public Optional<String> getDisplayName(_MatrixID userId) {
throw new NotImplementedException(this.getClass().getName());
}
@Override
public List<_ThreePid> getThreepids(_MatrixID userId) {
throw new NotImplementedException(this.getClass().getName());
}
@Override
public List<String> getRoles(_MatrixID userId) {
throw new NotImplementedException(this.getClass().getName());
}
}

View File

@@ -0,0 +1,27 @@
/*
* 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;
public abstract class ExecStore {
// no-op
}

View File

@@ -0,0 +1,343 @@
/*
* 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 org.apache.commons.lang3.StringUtils;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PostConstruct;
import java.util.*;
@Configuration
@ConfigurationProperties("exec")
public class ExecConfig {
public class Exit {
private List<Integer> success = Collections.singletonList(0);
private List<Integer> failure = Collections.singletonList(1);
public List<Integer> getSuccess() {
return success;
}
public void setSuccess(List<Integer> success) {
this.success = success;
}
public List<Integer> getFailure() {
return failure;
}
public void setFailure(List<Integer> failure) {
this.failure = failure;
}
}
public class TokenOverride {
private String localpart;
private String domain;
private String mxid;
private String password;
public String getLocalpart() {
return StringUtils.defaultIfEmpty(localpart, getToken().getLocalpart());
}
public void setLocalpart(String localpart) {
this.localpart = localpart;
}
public String getDomain() {
return StringUtils.defaultIfEmpty(domain, getToken().getDomain());
}
public void setDomain(String domain) {
this.domain = domain;
}
public String getMxid() {
return StringUtils.defaultIfEmpty(mxid, getToken().getMxid());
}
public void setMxid(String mxid) {
this.mxid = mxid;
}
public String getPassword() {
return StringUtils.defaultIfEmpty(password, getToken().getPassword());
}
public void setPassword(String password) {
this.password = password;
}
}
public class Token {
private String localpart = "{localpart}";
private String domain = "{domain}";
private String mxid = "{mxid}";
private String password = "{password}";
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;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
public class Process {
private TokenOverride token = new TokenOverride();
private String command;
private List<String> args = new ArrayList<>();
private Map<String, String> env = new HashMap<>();
private String input;
private Exit exit = new Exit();
private String output;
public TokenOverride getToken() {
return token;
}
public void setToken(TokenOverride token) {
this.token = token;
}
public String getCommand() {
return command;
}
public void setCommand(String command) {
this.command = command;
}
public List<String> getArgs() {
return args;
}
public void setArgs(List<String> args) {
this.args = args;
}
public Map<String, String> getEnv() {
return env;
}
public void setEnv(Map<String, String> env) {
this.env = env;
}
public void addEnv(String key, String value) {
this.env.put(key, value);
}
public String getInput() {
return input;
}
public void setInput(String input) {
this.input = input;
}
public Exit getExit() {
return exit;
}
public void setExit(Exit exit) {
this.exit = exit;
}
public String getOutput() {
return output;
}
public void setOutput(String output) {
this.output = output;
}
}
public class Auth extends Process {
private Boolean enabled;
public Boolean isEnabled() {
return enabled;
}
public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}
}
public class Directory extends Process {
private Boolean enabled;
public Boolean isEnabled() {
return enabled;
}
public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}
}
public class Identity extends Process {
private Boolean enabled;
public Boolean isEnabled() {
return enabled;
}
public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}
}
public class Profile extends Process {
private Boolean enabled;
public Boolean isEnabled() {
return enabled;
}
public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}
}
private boolean enabled;
private Token token = new Token();
private Auth auth = new Auth();
private Directory directory = new Directory();
private Identity identity = new Identity();
private Profile profile = new Profile();
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public Token getToken() {
return token;
}
public void setToken(Token token) {
this.token = token;
}
public Auth getAuth() {
return auth;
}
public void setAuth(Auth auth) {
this.auth = auth;
}
public Directory getDirectory() {
return directory;
}
public void setDirectory(Directory directory) {
this.directory = directory;
}
public Identity getIdentity() {
return identity;
}
public void setIdentity(Identity identity) {
this.identity = identity;
}
public Profile getProfile() {
return profile;
}
public void setProfile(Profile profile) {
this.profile = profile;
}
@PostConstruct
public void build() {
if (Objects.isNull(getAuth().isEnabled())) {
getAuth().setEnabled(isEnabled());
}
if (Objects.isNull(getDirectory().isEnabled())) {
getDirectory().setEnabled(isEnabled());
}
if (Objects.isNull(getIdentity().isEnabled())) {
getIdentity().setEnabled(isEnabled());
}
if (Objects.isNull(getProfile().isEnabled())) {
getProfile().setEnabled(isEnabled());
}
}
}

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.backend.exec;
import java.util.Arrays;
public class ExecAuthStoreArgsTest extends ExecAuthStoreTest {
@Override
protected void setValidCommand() {
cfg.getAuth().setCommand("src/test/resources/store/exec/authArgsTest.sh");
}
@Override
protected void setValidArgs() {
cfg.getAuth().setArgs(Arrays.asList(LocalpartToken, DomainToken, MxidToken, PassToken));
}
@Override
protected void setEmptyLocalpartConfig() {
cfg.getAuth().setArgs(Arrays.asList("", DomainToken, MxidToken, PassToken));
}
@Override
public void setWrongLocalpartConfig() {
cfg.getAuth().setArgs(Arrays.asList(LocalpartInvalid, DomainToken, MxidToken, PassToken));
}
@Override
protected void setEmptyDomainConfig() {
cfg.getAuth().setArgs(Arrays.asList(LocalpartToken, "", MxidToken, PassToken));
}
@Override
public void setWrongDomainConfig() {
cfg.getAuth().setArgs(Arrays.asList(LocalpartToken, DomainInvalid, MxidToken, PassToken));
}
@Override
protected void setEmptyMxidConfig() {
cfg.getAuth().setArgs(Arrays.asList(LocalpartToken, DomainToken, "", PassToken));
}
@Override
public void setWrongMxidConfig() {
cfg.getAuth().setArgs(Arrays.asList(LocalpartToken, DomainToken, MxidInvalid, PassToken));
}
}

View File

@@ -0,0 +1,72 @@
/*
* 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;
public class ExecAuthStoreEnvTest extends ExecAuthStoreTest {
private final String LocalpartEnv = "LOCALPART";
private final String DomainEnv = "DOMAIN";
private final String MxidEnv = "MXID";
@Override
protected void setValidCommand() {
cfg.getAuth().setCommand("src/test/resources/store/exec/authEnvTest.sh");
}
@Override
protected void setValidEnv() {
cfg.getAuth().addEnv(LocalpartEnv, LocalpartToken);
cfg.getAuth().addEnv(DomainEnv, DomainToken);
cfg.getAuth().addEnv(MxidEnv, MxidToken);
cfg.getAuth().addEnv("PASS", PassToken);
}
@Override
protected void setEmptyLocalpartConfig() {
cfg.getAuth().addEnv(LocalpartEnv, "");
}
@Override
public void setWrongLocalpartConfig() {
cfg.getAuth().addEnv(LocalpartEnv, LocalpartInvalid);
}
@Override
protected void setEmptyDomainConfig() {
cfg.getAuth().addEnv(DomainEnv, "");
}
@Override
public void setWrongDomainConfig() {
cfg.getAuth().addEnv(DomainEnv, DomainInvalid);
}
@Override
protected void setEmptyMxidConfig() {
cfg.getAuth().addEnv(MxidEnv, "");
}
@Override
public void setWrongMxidConfig() {
cfg.getAuth().addEnv(MxidEnv, MxidInvalid);
}
}

View File

@@ -0,0 +1,197 @@
/*
* 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.mxisd.UserIdType;
import io.kamax.mxisd.config.ExecConfig;
import org.apache.commons.lang3.RandomStringUtils;
import org.junit.Test;
import java.util.Arrays;
import java.util.Collections;
import static org.junit.Assert.assertEquals;
public abstract class ExecAuthStoreTest {
protected final ExecConfig cfg;
protected final ExecAuthStore p;
protected final String requiredPass = Long.toString(System.currentTimeMillis());
protected final String localpart = "user";
protected final String domain = "domain.tld";
protected final _MatrixID uId = MatrixID.from(localpart, domain).valid();
protected final String LocalpartToken = "{localpart}";
protected final String DomainToken = "{domain}";
protected final String MxidToken = "{mxid}";
protected final String PassToken = "{password}";
protected final String LocalpartInvalid = "@:";
protected final String DomainInvalid = "[.]:";
protected final String MxidInvalid = LocalpartInvalid + DomainInvalid;
protected final String PassInvalid = RandomStringUtils.randomAscii(20);
protected abstract void setValidCommand();
protected void setValidEnv() {
// no-op
}
protected void setValidArgs() {
// 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() {
setValidCommand();
setValidEnv();
setValidArgs();
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");
cfg.getAuth().addEnv("REQ_DOMAIN", uId.getDomain());
cfg.getAuth().addEnv("WITH_MXID", "1");
cfg.getAuth().addEnv("REQ_MXID", uId.getId());
cfg.getAuth().addEnv("REQ_PASS", requiredPass);
setValidConfig();
p = new ExecAuthStore(cfg);
}
@Test
public void validPassword() {
ExecAuthResult res = p.authenticate(uId, requiredPass);
assertEquals(true, res.isSuccess());
assertEquals(0, res.getExitStatus());
assertEquals(UserIdType.Localpart.getId(), res.getId().getType());
assertEquals(uId.getLocalPart(), res.getId().getValue());
}
@Test
public void invalidPassword() {
ExecAuthResult res = p.authenticate(uId, PassInvalid);
assertEquals(false, res.isSuccess());
assertEquals(1, res.getExitStatus());
}
@Test
public void emptyPassword() {
ExecAuthResult res = p.authenticate(uId, "");
assertEquals(false, res.isSuccess());
assertEquals(1, res.getExitStatus());
}
@Test(expected = NullPointerException.class)
public void nullPassword() {
p.authenticate(uId, null);
}
protected abstract void setEmptyLocalpartConfig();
@Test
public void doEmptyLocalpartConfig() {
setEmptyLocalpartConfig();
ExecAuthResult res = p.authenticate(uId, requiredPass);
assertEquals(false, res.isSuccess());
assertEquals(10, res.getExitStatus());
setValidConfig();
}
public abstract void setWrongLocalpartConfig();
@Test
public void wrongLocalpartConfig() {
setWrongLocalpartConfig();
ExecAuthResult res = p.authenticate(uId, requiredPass);
assertEquals(false, res.isSuccess());
assertEquals(20, res.getExitStatus());
setValidConfig();
}
protected abstract void setEmptyDomainConfig();
@Test
public void emptyDomainConfig() {
setEmptyDomainConfig();
ExecAuthResult res = p.authenticate(uId, requiredPass);
assertEquals(false, res.isSuccess());
assertEquals(11, res.getExitStatus());
setValidConfig();
}
public abstract void setWrongDomainConfig();
@Test
public void wrongDomainConfig() {
setWrongDomainConfig();
ExecAuthResult res = p.authenticate(uId, requiredPass);
assertEquals(false, res.isSuccess());
assertEquals(21, res.getExitStatus());
setValidConfig();
}
protected abstract void setEmptyMxidConfig();
@Test
public void emptyMxidConfig() {
setEmptyMxidConfig();
ExecAuthResult res = p.authenticate(uId, requiredPass);
assertEquals(false, res.isSuccess());
assertEquals(12, res.getExitStatus());
setValidConfig();
}
public abstract void setWrongMxidConfig();
@Test
public void wrongMxidConfig() {
setWrongMxidConfig();
ExecAuthResult res = p.authenticate(uId, requiredPass);
assertEquals(false, res.isSuccess());
assertEquals(22, res.getExitStatus());
setValidConfig();
}
}

View File

@@ -0,0 +1,41 @@
#!/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
[ -n "$1" ] || exit 10
[ "$1" = "$REQ_LOCALPART" ] || exit 20
shift
fi
if [ -n "$WITH_DOMAIN" ]; then
[ -n "$1" ] || exit 11
[ "$1" = "$REQ_DOMAIN" ] || exit 21
shift
fi
if [ -n "$WITH_MXID" ]; then
[ -n "$1" ] || exit 12
[ "$1" = "$REQ_MXID" ] || exit 22
shift
fi
[ "$1" = "$REQ_PASS" ] || exit 1
exit 0

View File

@@ -0,0 +1,38 @@
#!/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
[ -n "$LOCALPART" ] || exit 10
[ "$LOCALPART" = "$REQ_LOCALPART" ] || exit 20
fi
if [ -n "$WITH_DOMAIN" ]; then
[ -n "$DOMAIN" ] || exit 11
[ "$DOMAIN" = "$REQ_DOMAIN" ] || exit 21
fi
if [ -n "$WITH_MXID" ]; then
[ -n "$MXID" ] || exit 12
[ "$MXID" = "$REQ_MXID" ] || exit 22
fi
[ "$PASS" = "$REQ_PASS" ] || exit 1
exit 0