Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ANCHOR- 474] Update SEP10.home_domain to accommodate multi-tenancy #1169

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 5 additions & 8 deletions core/src/main/java/org/stellar/anchor/config/Sep10Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,20 @@ public interface Sep10Config {
/**
* The `web_auth_domain` property of <a
* href="https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0010.md#response">SEP-10</a>.
* If the `web_auth_domain` is not specified, the `web_auth_domain` will be set to the domain of
* the value of the `home_domain`. `web_auth_domain` value must be equal to the host of the SEP
* server.
* If the `web_auth_domain` is not specified, the `web_auth_domain` will be set to the first value
* of `home_domains`. The `web_auth_domain` value must equal to the host of the SEP server.
*
* @return the web auth domain.
*/
String getWebAuthDomain();

/**
* The `home_domain` property of <a
* The `home_domains` property of <a
* href="https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0010.md#request">SEP-10</a>.
* `home_domain` value must be equal to the host of the toml file. If sep1 is enabled, toml file
* will be hosted on the SEP server.
*
* @return the home domain.
* @return the list of home domains.
*/
String getHomeDomain();
List<String> getHomeDomains();

/**
* Set the authentication challenge transaction timeout in seconds. An expired signed transaction
Expand Down
27 changes: 14 additions & 13 deletions core/src/main/java/org/stellar/anchor/sep10/Sep10Service.java
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ public String validateChallengeTransactionHomeDomain(ChallengeTransaction challe
throw new SepValidationException("Invalid challenge transaction.");
}

if (!Objects.equals(sep10Config.getHomeDomain(), homeDomain)) {
if ((!sep10Config.getHomeDomains().contains(homeDomain))) {
throw new SepValidationException(format("Invalid home_domain. %s", homeDomain));
}

Expand Down Expand Up @@ -257,10 +257,11 @@ String fetchSigningKeyFromClientDomain(String clientDomain) throws SepException

void validateHomeDomain(ChallengeRequest request) throws SepValidationException {
String homeDomain = request.getHomeDomain();
String defaultHomeDomain = sep10Config.getHomeDomains().get(0);
if (homeDomain == null) {
debugF("home_domain is not specified. Will use the default: {}", sep10Config.getHomeDomain());
request.setHomeDomain(sep10Config.getHomeDomain());
} else if (!homeDomain.equalsIgnoreCase(sep10Config.getHomeDomain())) {
debugF("home_domain is not specified. Will use the default: {}", defaultHomeDomain);
request.setHomeDomain(defaultHomeDomain);
} else if (!sep10Config.getHomeDomains().contains(homeDomain)) {
infoF("Bad home_domain: {}", homeDomain);
throw new SepValidationException(format("home_domain [%s] is not supported.", homeDomain));
}
Expand Down Expand Up @@ -295,7 +296,7 @@ void validateChallengeRequest(
request.getTransaction(),
serverAccountId,
new Network(appConfig.getStellarNetworkPassphrase()),
sep10Config.getHomeDomain(),
sep10Config.getHomeDomains().toArray(new String[0]),
sep10Config.getWebAuthDomain(),
threshold,
signers);
Expand Down Expand Up @@ -349,7 +350,7 @@ AccountResponse fetchAccount(
request.getTransaction(),
serverAccountId,
new Network(appConfig.getStellarNetworkPassphrase()),
sep10Config.getHomeDomain(),
sep10Config.getHomeDomains().toArray(new String[0]),
sep10Config.getWebAuthDomain(),
signers);

Expand Down Expand Up @@ -395,7 +396,7 @@ ChallengeTransaction parseChallenge(ValidationRequest request)
transaction,
serverAccountId,
new Network(appConfig.getStellarNetworkPassphrase()),
sep10Config.getHomeDomain(),
sep10Config.getHomeDomains().toArray(new String[0]),
sep10Config.getWebAuthDomain());

debugF(
Expand Down Expand Up @@ -461,35 +462,35 @@ public synchronized ChallengeTransaction readChallengeTransaction(
String challengeXdr,
String serverAccountId,
Network network,
String domainName,
String[] domainNames,
String webAuthDomain)
throws InvalidSep10ChallengeException, IOException {
return Sep10Challenge.readChallengeTransaction(
challengeXdr, serverAccountId, network, domainName, webAuthDomain);
challengeXdr, serverAccountId, network, domainNames, webAuthDomain);
}

public synchronized void verifyChallengeTransactionSigners(
String challengeXdr,
String serverAccountId,
Network network,
String domainName,
String[] domainNames,
String webAuthDomain,
Set<String> signers)
throws InvalidSep10ChallengeException, IOException {
Sep10Challenge.verifyChallengeTransactionSigners(
challengeXdr, serverAccountId, network, domainName, webAuthDomain, signers);
challengeXdr, serverAccountId, network, domainNames, webAuthDomain, signers);
}

public synchronized void verifyChallengeTransactionThreshold(
String challengeXdr,
String serverAccountId,
Network network,
String domainName,
String[] domainNames,
String webAuthDomain,
int threshold,
Set<Sep10Challenge.Signer> signers)
throws InvalidSep10ChallengeException, IOException {
Sep10Challenge.verifyChallengeTransactionThreshold(
challengeXdr, serverAccountId, network, domainName, webAuthDomain, threshold, signers);
challengeXdr, serverAccountId, network, domainNames, webAuthDomain, threshold, signers);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ internal class Sep10ServiceTest {
every { sep10Config.webAuthDomain } returns TEST_WEB_AUTH_DOMAIN
every { sep10Config.authTimeout } returns 900
every { sep10Config.jwtTimeout } returns 900
every { sep10Config.homeDomain } returns TEST_HOME_DOMAIN
every { sep10Config.homeDomains } returns listOf(TEST_HOME_DOMAIN)

every { appConfig.stellarNetworkPassphrase } returns TESTNET.networkPassphrase

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public class PropertySep10Config implements Sep10Config, Validator {
private Boolean enabled;
private String webAuthDomain;
private String homeDomain;
private List<String> homeDomains;
private boolean clientAttributionRequired = false;
private List<String> clientAllowList = null;
private Integer authTimeout = 900;
Expand All @@ -50,8 +51,12 @@ public PropertySep10Config(

@PostConstruct
public void postConstruct() {
if (isEmpty(webAuthDomain)) {
webAuthDomain = homeDomain;
if (homeDomains == null || homeDomains.isEmpty()) {
homeDomains = List.of(homeDomain);
}
// If webAuthDomain is not specified and there is 1 and only 1 domain in the home_domains
if (isEmpty(webAuthDomain) && homeDomains.size() == 1) {
webAuthDomain = homeDomains.get(0);
JiahuiWho marked this conversation as resolved.
Show resolved Hide resolved
}
}

Expand Down Expand Up @@ -93,27 +98,22 @@ void validateConfig(Errors errors) {
"Please set the secret.sep10.jwt_secret or SECRET_SEP10_JWT_SECRET environment variable");
}

if (isEmpty(homeDomain)) {
// Only one of homeDomain or homeDomains can be defined.
if (isEmpty(homeDomain) && (homeDomains == null || homeDomains.isEmpty())) {
errors.rejectValue(
"homeDomain", "home-domain-empty", "The sep10.home_domain is not defined.");
} else if (!isEmpty(homeDomain) && (homeDomains != null && !homeDomains.isEmpty())) {
errors.rejectValue(
"homeDomain",
"home-domain-coexist",
"home_domain and home_domains cannot coexist. Please choose one to use.");
} else {
try {
new ManageDataOperation.Builder(String.format("%s %s", homeDomain, "auth"), new byte[64])
.build();
} catch (IllegalArgumentException iaex) {
errors.rejectValue(
"homeDomain",
"sep10-home-domain-too-long",
format(
"The sep10.home_domain (%s) is longer than the maximum length (64) of a domain. Error=%s",
homeDomain, iaex));
}

if (!NetUtil.isServerPortValid(homeDomain, false)) {
errors.rejectValue(
"homeDomain",
"sep10-home-domain-invalid",
"The sep10.home_domain does not have valid format.");
if (!isEmpty(homeDomain)) {
validateDomain(errors, homeDomain);
} else {
for (String domain : homeDomains) {
validateDomain(errors, domain);
}
}
}

Expand All @@ -135,6 +135,11 @@ void validateConfig(Errors errors) {
"sep10-web-auth-domain-invalid",
"The sep10.web_auth_domain does not have valid format.");
}
} else if (homeDomains != null && homeDomains.size() > 1) {
errors.rejectValue(
"webAuthDomain",
"sep10-web-auth-domain-empty",
"The sep10.web_auth_domain is required for multiple home domains.");
}

if (authTimeout <= 0) {
Expand Down Expand Up @@ -191,6 +196,26 @@ void validateCustodialAccounts(Errors errors) {
}
}

private void validateDomain(Errors errors, String domain) {
try {
new ManageDataOperation.Builder(String.format("%s %s", domain, "auth"), new byte[64]).build();
} catch (IllegalArgumentException iaex) {
errors.rejectValue(
"homeDomain",
"sep10-home-domain-too-long",
format(
"The sep10.home_domain (%s) is longer than the maximum length (64) of a domain. Error=%s",
domain, iaex));
}

if (!NetUtil.isServerPortValid(domain, false)) {
errors.rejectValue(
"homeDomain",
"sep10-home-domain-invalid",
"The sep10.home_domain does not have valid format.");
}
}

@Override
public List<String> getAllowedClientDomains() {
// if clientAllowList is not defined, all client domains from the clients section are allowed.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -315,12 +315,23 @@ sep10:
enabled: false
#
# The `web_auth_domain` property of SEP-10. https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0010.md#response
# If the `web_auth_domain` is not specified, the `web_auth_domain` will be set to the domain of the value of the `home_domain`.
# `web_auth_domain` value must equal to the host of the SEP server.
# The `web_auth_domain` is optional and will be set to the value of the `home_domain` or `home_domains` if
# 1) the `home_domain` is in use,
# 2) or the `home_domains` is in use and has only one value
# The `web_auth_domain` is required if
# 1) the `home_domains` is in use has more than one value
web_auth_domain:
# The `home_domain` property of SEP-10. https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0010.md#request
# `home_domain` value must be equal to the host of the toml file. If sep1 is enabled, toml file will be hosted on the SEP server.
# This property cannot coexist with `home_domains` and is going to be deprecated. Please use `home_domains` instead.
home_domain: localhost:8080
# The `home_domains` property of SEP-10. This is a list of domains that the client can use to authenticate.
# https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0010.md#request
# This property cannot coexist with `home_domain`. Please also set web_auth_domain if you have more than one home domain in this list.
# The following lists are examples:
# Ex: home_domains: [ap.stellar.org, sdp.stellar.org]
# Ex: home_domains: ap.stellar.org, sdp.stellar.org
home_domains:
# Set if the client attribution is required. Client Attribution requires clients to verify their identity by passing
# a domain in the challenge transaction request and signing the challenge with the ``SIGNING_KEY`` on that domain's
# SEP-1 stellar.toml. See the SEP-10 section `Verifying Client Application Identity` for more information
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ sep10.client_allow_list:
sep10.client_attribution_required:
sep10.enabled:
sep10.home_domain:
sep10.home_domains:
sep10.jwt_timeout:
sep10.known_custodial_account_required:
sep10.web_auth_domain:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@ package org.stellar.anchor.platform.config

import io.mockk.every
import io.mockk.mockk
import java.util.stream.Stream
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.CsvSource
import org.junit.jupiter.params.provider.NullSource
import org.junit.jupiter.params.provider.ValueSource
import org.junit.jupiter.params.provider.*
import org.springframework.validation.BindException
import org.springframework.validation.Errors
import org.stellar.anchor.config.AppConfig
Expand Down Expand Up @@ -217,4 +216,38 @@ class Sep10ConfigTest {
config.postConstruct()
assertEquals("localhost:8080", config.webAuthDomain)
}

@ParameterizedTest
@MethodSource("generatedHomeDomainsTestConfig")
fun `test web_auth_domain, home_domain and home_domains in valid config format`(
webAuthDomain: String?,
homeDomain: String?,
homeDomains: List<String>?,
hasError: Boolean,
numberOfHomeDomains: Int
) {
config.webAuthDomain = webAuthDomain
config.homeDomain = homeDomain
config.homeDomains = homeDomains

config.validateConfig(errors)
assertEquals(hasError, errors.hasErrors())

if (!hasError) {
config.postConstruct()
assertEquals(numberOfHomeDomains, config.homeDomains.size)
}
}

companion object {
@JvmStatic
fun generatedHomeDomainsTestConfig(): Stream<Arguments> {
return Stream.of(
Arguments.of(null, null, null, true, 0),
Arguments.of(null, "www.stellar.org", listOf("www.stellar.org", "www.losbstr.co"), true, 0),
Arguments.of(null, "www.stellar.org", emptyList<String>(), false, 1),
Arguments.of("localhost:8080", "", listOf("www.stellar.org", "www.losbstr.co"), false, 2),
)
}
}
}
Loading