User Directory support in REST Backend
This commit is contained in:
@@ -91,9 +91,13 @@ Content-type: JSON UTF-8
|
|||||||
#### Request Body
|
#### Request Body
|
||||||
```
|
```
|
||||||
{
|
{
|
||||||
|
"by": "<search type>",
|
||||||
"search_term": "doe"
|
"search_term": "doe"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
`by` can be:
|
||||||
|
- `name`
|
||||||
|
- `threepid`
|
||||||
|
|
||||||
#### Response Body:
|
#### Response Body:
|
||||||
If users found:
|
If users found:
|
||||||
@@ -102,8 +106,8 @@ If users found:
|
|||||||
"limited": false,
|
"limited": false,
|
||||||
"results": [
|
"results": [
|
||||||
{
|
{
|
||||||
"display_name": "John Doe",
|
|
||||||
"avatar_url": "http://domain.tld/path/to/avatar.png",
|
"avatar_url": "http://domain.tld/path/to/avatar.png",
|
||||||
|
"display_name": "John Doe",
|
||||||
"user_id": "UserIdLocalpart"
|
"user_id": "UserIdLocalpart"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@@ -0,0 +1,82 @@
|
|||||||
|
/*
|
||||||
|
* mxisd - Matrix Identity Server Daemon
|
||||||
|
* Copyright (C) 2017 Maxime Dor
|
||||||
|
*
|
||||||
|
* https://max.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.rest;
|
||||||
|
|
||||||
|
import io.kamax.matrix.MatrixID;
|
||||||
|
import io.kamax.mxisd.config.MatrixConfig;
|
||||||
|
import io.kamax.mxisd.config.rest.RestBackendConfig;
|
||||||
|
import io.kamax.mxisd.controller.directory.v1.io.UserDirectorySearchRequest;
|
||||||
|
import io.kamax.mxisd.controller.directory.v1.io.UserDirectorySearchResult;
|
||||||
|
import io.kamax.mxisd.directory.IDirectoryProvider;
|
||||||
|
import io.kamax.mxisd.exception.InternalServerError;
|
||||||
|
import io.kamax.mxisd.util.RestClientUtils;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
public class RestDirectoryProvider extends RestProvider implements IDirectoryProvider {
|
||||||
|
|
||||||
|
private MatrixConfig mxCfg;
|
||||||
|
|
||||||
|
public RestDirectoryProvider(RestBackendConfig cfg, MatrixConfig mxCfg) {
|
||||||
|
super(cfg);
|
||||||
|
this.mxCfg = mxCfg;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return cfg.isEnabled() && StringUtils.isNotBlank(cfg.getEndpoints().getDirectory());
|
||||||
|
}
|
||||||
|
|
||||||
|
private UserDirectorySearchResult search(String by, String query) {
|
||||||
|
UserDirectorySearchRequest request = new UserDirectorySearchRequest(query);
|
||||||
|
request.setBy(by);
|
||||||
|
try (CloseableHttpResponse httpResponse = client.execute(RestClientUtils.post(cfg.getEndpoints().getDirectory(), request))) {
|
||||||
|
int status = httpResponse.getStatusLine().getStatusCode();
|
||||||
|
if (status < 200 || status >= 300) {
|
||||||
|
throw new InternalServerError("REST backend: Error: " + IOUtils.toString(httpResponse.getEntity().getContent(), StandardCharsets.UTF_8));
|
||||||
|
}
|
||||||
|
|
||||||
|
UserDirectorySearchResult response = parser.parse(httpResponse, UserDirectorySearchResult.class);
|
||||||
|
for (UserDirectorySearchResult.Result result : response.getResults()) {
|
||||||
|
result.setUserId(new MatrixID(result.getUserId(), mxCfg.getDomain()).getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new InternalServerError("REST backend: I/O error: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserDirectorySearchResult searchByDisplayName(String query) {
|
||||||
|
return search("name", query);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserDirectorySearchResult searchBy3pid(String query) {
|
||||||
|
return search("threepid", query);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -22,12 +22,21 @@ package io.kamax.mxisd.controller.directory.v1.io;
|
|||||||
|
|
||||||
public class UserDirectorySearchRequest {
|
public class UserDirectorySearchRequest {
|
||||||
|
|
||||||
|
private String by;
|
||||||
private String searchTerm;
|
private String searchTerm;
|
||||||
|
|
||||||
public UserDirectorySearchRequest(String searchTerm) {
|
public UserDirectorySearchRequest(String searchTerm) {
|
||||||
setSearchTerm(searchTerm);
|
setSearchTerm(searchTerm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getBy() {
|
||||||
|
return by;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBy(String by) {
|
||||||
|
this.by = by;
|
||||||
|
}
|
||||||
|
|
||||||
public String getSearchTerm() {
|
public String getSearchTerm() {
|
||||||
return searchTerm;
|
return searchTerm;
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,167 @@
|
|||||||
|
/*
|
||||||
|
* mxisd - Matrix Identity Server Daemon
|
||||||
|
* Copyright (C) 2017 Maxime Dor
|
||||||
|
*
|
||||||
|
* https://max.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.rest;
|
||||||
|
|
||||||
|
import com.github.tomakehurst.wiremock.junit.WireMockRule;
|
||||||
|
import io.kamax.matrix.MatrixID;
|
||||||
|
import io.kamax.mxisd.config.MatrixConfig;
|
||||||
|
import io.kamax.mxisd.config.rest.RestBackendConfig;
|
||||||
|
import io.kamax.mxisd.controller.directory.v1.io.UserDirectorySearchResult;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
import static com.github.tomakehurst.wiremock.client.WireMock.*;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
public class RestDirectoryProviderTest {
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public WireMockRule wireMockRule = new WireMockRule(65000);
|
||||||
|
|
||||||
|
private RestDirectoryProvider p;
|
||||||
|
|
||||||
|
private String domain = "example.org";
|
||||||
|
private String endpoint = "/directory/search";
|
||||||
|
private String byNameSearch = "doe";
|
||||||
|
private String byNameAvatar = "http://domain.tld/path/to/avatar.png";
|
||||||
|
private String byNameDisplay = "John Doe";
|
||||||
|
private String byNameId = "john.doe";
|
||||||
|
private String byNameRequest = "{\"by\":\"name\",\"search_term\":\"" + byNameSearch + "\"}";
|
||||||
|
private String byNameResponse = "{\"limited\":false,\"results\":[{\"avatar_url\":\"" + byNameAvatar +
|
||||||
|
"\",\"display_name\":\"" + byNameDisplay + "\",\"user_id\":\"" + byNameId + "\"}]}";
|
||||||
|
private String byNameEmptyResponse = "{\"limited\":false,\"results\":[]}";
|
||||||
|
|
||||||
|
private String byThreepidSearch = "jane";
|
||||||
|
private String byThreepidAvatar = "http://domain.tld/path/to/avatar.png";
|
||||||
|
private String byThreepidDisplay = "John Doe";
|
||||||
|
private String byThreepidId = "john.doe";
|
||||||
|
private String byThreepidRequest = "{\"by\":\"threepid\",\"search_term\":\"" + byThreepidSearch + "\"}";
|
||||||
|
private String byThreepidResponse = "{\"limited\":false,\"results\":[{\"avatar_url\":\"" + byThreepidAvatar +
|
||||||
|
"\",\"display_name\":\"" + byThreepidDisplay + "\",\"user_id\":\"" + byThreepidId + "\"}]}";
|
||||||
|
private String byThreepidEmptyResponse = "{\"limited\":false,\"results\":[]}";
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void before() {
|
||||||
|
MatrixConfig mxCfg = new MatrixConfig();
|
||||||
|
mxCfg.setDomain(domain);
|
||||||
|
mxCfg.build();
|
||||||
|
|
||||||
|
RestBackendConfig cfg = new RestBackendConfig();
|
||||||
|
cfg.setEnabled(true);
|
||||||
|
cfg.setHost("http://localhost:65000");
|
||||||
|
cfg.getEndpoints().setDirectory(endpoint);
|
||||||
|
cfg.build();
|
||||||
|
|
||||||
|
p = new RestDirectoryProvider(cfg, mxCfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void byNameFound() {
|
||||||
|
stubFor(post(urlEqualTo(endpoint))
|
||||||
|
.willReturn(aResponse()
|
||||||
|
.withHeader("Content-Type", "application/json")
|
||||||
|
.withBody(byNameResponse)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
UserDirectorySearchResult result = p.searchByDisplayName(byNameSearch);
|
||||||
|
assertTrue(!result.isLimited());
|
||||||
|
assertTrue(result.getResults().size() == 1);
|
||||||
|
UserDirectorySearchResult.Result entry = result.getResults().get(0);
|
||||||
|
assertNotNull(entry);
|
||||||
|
assertTrue(StringUtils.equals(byNameAvatar, entry.getAvatarUrl()));
|
||||||
|
assertTrue(StringUtils.equals(byNameDisplay, entry.getDisplayName()));
|
||||||
|
assertTrue(StringUtils.equals(new MatrixID(byNameId, domain).getId(), entry.getUserId()));
|
||||||
|
|
||||||
|
verify(postRequestedFor(urlMatching(endpoint))
|
||||||
|
.withHeader("Content-Type", containing("application/json"))
|
||||||
|
.withRequestBody(equalTo(byNameRequest))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void byNameNotFound() {
|
||||||
|
stubFor(post(urlEqualTo(endpoint))
|
||||||
|
.willReturn(aResponse()
|
||||||
|
.withHeader("Content-Type", "application/json")
|
||||||
|
.withBody(byNameEmptyResponse)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
UserDirectorySearchResult result = p.searchByDisplayName(byNameSearch);
|
||||||
|
assertTrue(!result.isLimited());
|
||||||
|
assertTrue(result.getResults().isEmpty());
|
||||||
|
|
||||||
|
verify(postRequestedFor(urlMatching(endpoint))
|
||||||
|
.withHeader("Content-Type", containing("application/json"))
|
||||||
|
.withRequestBody(equalTo(byNameRequest))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void byThreepidFound() {
|
||||||
|
stubFor(post(urlEqualTo(endpoint))
|
||||||
|
.willReturn(aResponse()
|
||||||
|
.withHeader("Content-Type", "application/json")
|
||||||
|
.withBody(new String(byThreepidResponse.getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
UserDirectorySearchResult result = p.searchBy3pid(byThreepidSearch);
|
||||||
|
assertTrue(!result.isLimited());
|
||||||
|
assertTrue(result.getResults().size() == 1);
|
||||||
|
UserDirectorySearchResult.Result entry = result.getResults().get(0);
|
||||||
|
assertNotNull(entry);
|
||||||
|
assertTrue(StringUtils.equals(byThreepidAvatar, entry.getAvatarUrl()));
|
||||||
|
assertTrue(StringUtils.equals(byThreepidDisplay, entry.getDisplayName()));
|
||||||
|
assertTrue(StringUtils.equals(new MatrixID(byThreepidId, domain).getId(), entry.getUserId()));
|
||||||
|
|
||||||
|
verify(postRequestedFor(urlMatching(endpoint))
|
||||||
|
.withHeader("Content-Type", containing("application/json"))
|
||||||
|
.withRequestBody(equalTo(byThreepidRequest))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void byThreepidNotFound() {
|
||||||
|
stubFor(post(urlEqualTo(endpoint))
|
||||||
|
.willReturn(aResponse()
|
||||||
|
.withHeader("Content-Type", "application/json")
|
||||||
|
.withBody(byThreepidEmptyResponse)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
UserDirectorySearchResult result = p.searchBy3pid(byThreepidSearch);
|
||||||
|
assertTrue(!result.isLimited());
|
||||||
|
assertTrue(result.getResults().isEmpty());
|
||||||
|
|
||||||
|
verify(postRequestedFor(urlMatching(endpoint))
|
||||||
|
.withHeader("Content-Type", containing("application/json"))
|
||||||
|
.withRequestBody(equalTo(byThreepidRequest))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -53,7 +53,7 @@ public class RestThreePidProviderTest {
|
|||||||
cfg.setEnabled(true);
|
cfg.setEnabled(true);
|
||||||
cfg.setHost("http://localhost:65000");
|
cfg.setHost("http://localhost:65000");
|
||||||
cfg.getEndpoints().getIdentity().setSingle(lookupSinglePath);
|
cfg.getEndpoints().getIdentity().setSingle(lookupSinglePath);
|
||||||
cfg.getEndpoints().getIdentity().setBulk("/lookup/bulk");
|
cfg.getEndpoints().getIdentity().setBulk(lookupBulkPath);
|
||||||
cfg.build();
|
cfg.build();
|
||||||
|
|
||||||
p = new RestThreePidProvider(cfg, mxCfg);
|
p = new RestThreePidProvider(cfg, mxCfg);
|
||||||
|
Reference in New Issue
Block a user