소스 검색

Merge branch 'master' into kris/add_support_for_restore_ui

# Conflicts:
#	bubble-server/src/main/java/bubble/server/BubbleConfiguration.java
#	bubble-web
pull/20/head
Kristijan Mitrovic 4 년 전
부모
커밋
e5b21ce537
27개의 변경된 파일12463개의 추가작업 그리고 96개의 파일을 삭제
  1. +13
    -1
      bubble-server/src/main/java/bubble/ApiConstants.java
  2. +1
    -1
      bubble-server/src/main/java/bubble/dao/account/AccountDAO.java
  3. +16
    -9
      bubble-server/src/main/java/bubble/dao/account/AccountInitializer.java
  4. +12
    -0
      bubble-server/src/main/java/bubble/dao/account/message/AccountMessageDAO.java
  5. +1
    -0
      bubble-server/src/main/java/bubble/dao/cloud/BubbleNetworkDAO.java
  6. +4
    -0
      bubble-server/src/main/java/bubble/model/account/AccountLoginRequest.java
  7. +11
    -7
      bubble-server/src/main/java/bubble/model/account/message/AccountMessage.java
  8. +5
    -0
      bubble-server/src/main/java/bubble/model/bill/AccountPlan.java
  9. +21
    -15
      bubble-server/src/main/java/bubble/model/cloud/BubbleNetwork.java
  10. +1
    -1
      bubble-server/src/main/java/bubble/resources/account/AuthResource.java
  11. +13
    -1
      bubble-server/src/main/java/bubble/resources/bill/AccountPlansResource.java
  12. +4
    -5
      bubble-server/src/main/java/bubble/server/BubbleConfiguration.java
  13. +7
    -2
      bubble-server/src/main/java/bubble/service/account/StandardAccountMessageService.java
  14. +1
    -1
      bubble-server/src/main/resources/META-INF/bubble/bubble.properties
  15. +4
    -0
      bubble-server/src/main/resources/bubble-config.yml
  16. +12245
    -0
      bubble-server/src/main/resources/bubble/host-prefixes.txt
  17. +4
    -2
      bubble-server/src/main/resources/message_templates/en_US/email/notice/welcome/account/message.hbs
  18. +1
    -9
      bubble-server/src/main/resources/message_templates/en_US/email/request/verify/account/message.hbs
  19. +3
    -3
      bubble-server/src/main/resources/message_templates/en_US/server/post_auth/ResourceMessages.properties
  20. +8
    -0
      bubble-server/src/main/resources/message_templates/en_US/server/pre_auth/ResourceMessages.properties
  21. +1
    -1
      bubble-server/src/main/resources/message_templates/en_US/sms/notice/welcome/account/message.hbs
  22. +2
    -1
      bubble-server/src/main/resources/message_templates/en_US/sms/request/verify/account/message.hbs
  23. +2
    -2
      bubble-server/src/test/resources/models/tests/auth/account_registration.json
  24. +80
    -32
      bubble-server/src/test/resources/models/tests/auth/multifactor_auth.json
  25. +1
    -1
      bubble-web
  26. +1
    -1
      utils/cobbzilla-utils
  27. +1
    -1
      utils/cobbzilla-wizard

+ 13
- 1
bubble-server/src/main/java/bubble/ApiConstants.java 파일 보기

@@ -11,6 +11,7 @@ import com.warrenstrange.googleauth.GoogleAuthenticator;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomUtils;
import org.cobbzilla.util.daemon.ZillaRuntime;
import org.cobbzilla.util.io.FileUtil;
import org.glassfish.grizzly.http.server.Request;
import org.glassfish.jersey.server.ContainerRequest;
@@ -232,7 +233,10 @@ public class ApiConstants {
return val == null ? null : val.textValue();
}

@Getter(lazy=true) private static final String[] hostPrefixes = stream2string("bubble/host-prefixes.txt").split("\n");
@Getter(lazy=true) private static final String[] hostPrefixes = Arrays.stream(stream2string("bubble/host-prefixes.txt")
.split("\n"))
.filter(ZillaRuntime::notEmpty)
.toArray(String[]::new);

public static String newNodeHostname() {
final String rand0 = getHostPrefixes()[RandomUtils.nextInt(0, getHostPrefixes().length)];
@@ -243,6 +247,14 @@ public class ApiConstants {
return rand0+"-"+(rand1 < 10 ? "0"+rand1 : rand1)+rand2+"-"+rand3+rand4;
}

public static String newNetworkName() {
final String rand0 = getHostPrefixes()[RandomUtils.nextInt(0, getHostPrefixes().length)];
final String rand1 = randomAlphanumeric(2).toLowerCase() + RandomUtils.nextInt(10, 100) + randomAlphanumeric(1).toLowerCase();
final String rand2 = randomAlphanumeric(2).toLowerCase() + RandomUtils.nextInt(10, 100) + randomAlphanumeric(1).toLowerCase();
final String rand3 = randomAlphanumeric(2).toLowerCase() + RandomUtils.nextInt(10, 100) + randomAlphanumeric(1).toLowerCase();
return rand0+"-"+rand1+"-"+rand2+"-"+rand3;
}

public static String getRemoteHost(Request req) {
final String xff = req.getHeader("X-Forwarded-For");
final String remoteHost = xff == null ? req.getRemoteAddr() : xff;


+ 1
- 1
bubble-server/src/main/java/bubble/dao/account/AccountDAO.java 파일 보기

@@ -162,7 +162,7 @@ public class AccountDAO extends AbstractCRUDDAO<Account> implements SqlViewSearc
}

if (account.hasParent()) {
final AccountInitializer init = new AccountInitializer(account, this, messageDAO, selfNodeService);
final AccountInitializer init = new AccountInitializer(account, this, policyDAO, messageDAO, selfNodeService);
account.setAccountInitializer(init);
daemon(init);
}


+ 16
- 9
bubble-server/src/main/java/bubble/dao/account/AccountInitializer.java 파일 보기

@@ -6,6 +6,7 @@ package bubble.dao.account;

import bubble.dao.account.message.AccountMessageDAO;
import bubble.model.account.Account;
import bubble.model.account.AccountPolicy;
import bubble.model.account.message.AccountAction;
import bubble.model.account.message.AccountMessage;
import bubble.model.account.message.AccountMessageType;
@@ -31,6 +32,7 @@ public class AccountInitializer implements Runnable {

private Account account;
private AccountDAO accountDAO;
private AccountPolicyDAO policyDAO;
private AccountMessageDAO messageDAO;
private SelfNodeService selfNodeService;

@@ -50,9 +52,14 @@ public class AccountInitializer implements Runnable {
public Exception getError() { return error.get(); }
public boolean hasError () { return getError() != null; }

public AccountInitializer(Account account, AccountDAO accountDAO, AccountMessageDAO messageDAO, SelfNodeService selfNodeService) {
public AccountInitializer(Account account,
AccountDAO accountDAO,
AccountPolicyDAO policyDAO,
AccountMessageDAO messageDAO,
SelfNodeService selfNodeService) {
this.account = account;
this.accountDAO = accountDAO;
this.policyDAO = policyDAO;
this.messageDAO = messageDAO;
this.selfNodeService = selfNodeService;
}
@@ -73,10 +80,6 @@ public class AccountInitializer implements Runnable {
log.warn("aborting!");
return;
}

if (account.hasPolicy() && account.getPolicy().hasAccountContacts()) {
messageDAO.sendVerifyRequest(account.getRemoteHost(), account, account.getPolicy().getAccountContacts()[0]);
}
success = true;
break;
} catch (Exception e) {
@@ -87,18 +90,22 @@ public class AccountInitializer implements Runnable {
if (!success) throw lastEx;
if (account.sendWelcomeEmail()) {
final BubbleNetwork thisNetwork = selfNodeService.getThisNetwork();
final String accountUuid = account.getUuid();
final AccountPolicy policy = policyDAO.findSingleByAccount(accountUuid);
final String contact = policy != null && policy.hasAccountContacts() ? policy.getAccountContacts()[0].getUuid() : null;
if (contact == null) die("no contact found for welcome message: account="+accountUuid);
messageDAO.create(new AccountMessage()
.setRemoteHost(account.getRemoteHost())
.setAccount(account.getUuid())
.setName(account.getUuid())
.setAccount(accountUuid)
.setName(accountUuid)
.setNetwork(thisNetwork.getUuid())
.setMessageType(AccountMessageType.notice)
.setAction(AccountAction.welcome)
.setTarget(ActionTarget.account));
.setTarget(ActionTarget.account)
.setContact(contact));
}
} catch (Exception e) {
error.set(e);
// todo: send to errbit
die("error: "+e, e);
} finally {
completed.set(true);


+ 12
- 0
bubble-server/src/main/java/bubble/dao/account/message/AccountMessageDAO.java 파일 보기

@@ -166,6 +166,9 @@ public class AccountMessageDAO extends AccountOwnedEntityDAO<AccountMessage> {
}

public AccountMessage findOperationRequest(AccountMessage basis) {
if (basis.getAction() == AccountAction.welcome && basis.getTarget() == ActionTarget.account) {
return findWelcomeNotice(basis);
}
return findByUniqueFields("account", basis.getAccount(),
"name", basis.getName(),
"requestId", basis.getRequestId(),
@@ -174,6 +177,15 @@ public class AccountMessageDAO extends AccountOwnedEntityDAO<AccountMessage> {
"target", basis.getTarget());
}

public AccountMessage findWelcomeNotice(AccountMessage basis) {
return findByUniqueFields("account", basis.getAccount(),
"name", basis.getName(),
"requestId", basis.getRequestId(),
"messageType", AccountMessageType.notice,
"action", AccountAction.welcome,
"target", ActionTarget.account);
}

public List<AccountMessage> findOperationDenials(AccountMessage basis) {
if (basis == null) {
return Collections.emptyList();


+ 1
- 0
bubble-server/src/main/java/bubble/dao/cloud/BubbleNetworkDAO.java 파일 보기

@@ -45,6 +45,7 @@ public class BubbleNetworkDAO extends AccountOwnedEntityDAO<BubbleNetwork> {
if (errors.isInvalid()) throw invalidEx(errors);
if (errors.hasSuggestedName()) network.setName(errors.getSuggestedName());
}
if (!network.hasNickname()) network.setNickname(network.getName());
final AnsibleInstallType installType = network.hasForkHost() && configuration.isSageLauncher()
? AnsibleInstallType.sage
: AnsibleInstallType.node;


+ 4
- 0
bubble-server/src/main/java/bubble/model/account/AccountLoginRequest.java 파일 보기

@@ -1,3 +1,7 @@
/**
* Copyright (c) 2020 Bubble, Inc. All rights reserved.
* For personal (non-commercial) use, see license: https://getbubblenow.com/bubble-license/
*/
package bubble.model.account;

import org.cobbzilla.wizard.auth.LoginRequest;


+ 11
- 7
bubble-server/src/main/java/bubble/model/account/message/AccountMessage.java 파일 보기

@@ -97,13 +97,17 @@ public class AccountMessage extends IdentifiableBase implements HasAccount {
public String templateName(String basename) { return getMessageType()+"/"+ getAction()+"/"+getTarget()+"/"+basename+".hbs"; }

public long tokenTimeoutSeconds(AccountPolicy policy) {
if (getMessageType() != AccountMessageType.request) return -1;
switch (getTarget()) {
case account: return policy.getAccountOperationTimeout()/1000;
case network: return policy.getNodeOperationTimeout()/1000;
default:
log.warn("tokenTimeout: invalid target: "+getTarget());
return -1;
// only requests and welcome message get tokens (welcome messages also verify the initial email address)
if (getMessageType() == AccountMessageType.request || (getMessageType() == AccountMessageType.notice && getAction() == AccountAction.welcome)) {
switch (getTarget()) {
case account: return policy.getAccountOperationTimeout()/1000;
case network: return policy.getNodeOperationTimeout()/1000;
default:
log.warn("tokenTimeout: invalid target: "+getTarget());
return -1;
}
} else {
return -1;
}
}



+ 5
- 0
bubble-server/src/main/java/bubble/model/bill/AccountPlan.java 파일 보기

@@ -132,6 +132,10 @@ public class AccountPlan extends IdentifiableBase implements HasNetwork {
@Getter @Setter private String refundError;

// Fields below are used when creating a new plan, to also create the network associated with it
@Size(max=NAME_MAXLEN, message="err.nick.tooLong")
@Transient @Getter @Setter private transient String nickname;
public boolean hasNickname () { return !empty(nickname); }

@Size(max=10000, message="err.description.length")
@Transient @Getter @Setter private transient String description;

@@ -171,6 +175,7 @@ public class AccountPlan extends IdentifiableBase implements HasNetwork {
CloudService storage) {
return new BubbleNetwork()
.setName(getName())
.setNickname(getNickname())
.setDescription(getDescription())
.setLocale(getLocale())
.setTimezone(getTimezone())


+ 21
- 15
bubble-server/src/main/java/bubble/model/cloud/BubbleNetwork.java 파일 보기

@@ -61,7 +61,7 @@ import static org.cobbzilla.wizard.model.crypto.EncryptedTypes.ENC_PAD;
public class BubbleNetwork extends IdentifiableBase implements HasNetwork, HasBubbleTags<BubbleNetwork> {

public static final String[] UPDATE_FIELDS = {
"footprint", "description", "locale", "timezone", "state", "syncPassword", "launchLock"
"nickname", "footprint", "description", "locale", "timezone", "state", "syncPassword", "launchLock"
};
public static final String[] CREATE_FIELDS = ArrayUtil.append(UPDATE_FIELDS,
"name", "domain", "sendErrors", "sendMetrics");
@@ -110,7 +110,13 @@ public class BubbleNetwork extends IdentifiableBase implements HasNetwork, HasBu

@Transient @JsonIgnore public String getNetworkDomain () { return name + "." + domainName; }

@Column(nullable=false) @ECField(index=50)
@ECSearchable(filter=true) @ECField(index=50)
@ECIndex @Column(nullable=false, length=NAME_MAXLEN)
@Size(min=1, max=NAME_MAXLEN, message="err.nick.tooLong")
@Getter @Setter private String nickname;
public boolean hasNickname () { return !empty(nickname); }

@Column(nullable=false) @ECField(index=60)
@Getter @Setter private Integer sslPort;

@Transient @JsonIgnore public String getPublicUri() {
@@ -121,37 +127,37 @@ public class BubbleNetwork extends IdentifiableBase implements HasNetwork, HasBu
return getUuid().equals(ROOT_NETWORK_UUID) ? configuration.getPublicUriBase() : getPublicUri();
}

@ECIndex @Column(nullable=false, updatable=false, length=60)
@ECIndex @Column(nullable=false, updatable=false, length=60) @ECField(index=70)
@Enumerated(EnumType.STRING)
@Getter @Setter private AnsibleInstallType installType;

@ECSearchable @ECField(index=70)
@ECSearchable @ECField(index=80)
@ECForeignKey(entity=AccountSshKey.class)
@Column(length=UUID_MAXLEN)
@Getter @Setter private String sshKey;
public boolean hasSshKey () { return !empty(sshKey); }

@ECSearchable @ECField(index=80)
@ECSearchable @ECField(index=90)
@ECIndex @Column(nullable=false, updatable=false, length=20)
@Enumerated(EnumType.STRING) @Getter @Setter private ComputeNodeSizeType computeSizeType;

@ECSearchable @ECField(index=90)
@ECSearchable @ECField(index=100)
@ECForeignKey(entity=BubbleFootprint.class)
@Column(nullable=false, length=UUID_MAXLEN)
@Getter @Setter private String footprint;
public boolean hasFootprint () { return footprint != null; }

@ECSearchable @ECField(index=100)
@ECSearchable @ECField(index=110)
@ECForeignKey(entity=CloudService.class)
@Column(nullable=false, updatable=false, length=UUID_MAXLEN)
@Getter @Setter private String storage;

@ECSearchable(filter=true) @ECField(index=110)
@ECSearchable(filter=true) @ECField(index=120)
@Size(max=10000, message="err.description.length")
@Type(type=ENCRYPTED_STRING) @Column(columnDefinition="varchar("+(10000+ENC_PAD)+")")
@Getter @Setter private String description;

@ECSearchable @ECField(index=120)
@ECSearchable @ECField(index=130)
@Size(max=20, message="err.locale.length")
@Type(type=ENCRYPTED_STRING) @Column(columnDefinition="varchar("+(20+ENC_PAD)+") NOT NULL")
@Getter @Setter private String locale = getDEFAULT_LOCALE();
@@ -159,27 +165,27 @@ public class BubbleNetwork extends IdentifiableBase implements HasNetwork, HasBu

// A unicode timezone alias from: cobbzilla-utils/src/main/resources/org/cobbzilla/util/time/unicode-timezones.xml
// All unicode aliases are guaranteed to map to a Linux timezone and a Java timezone
@ECSearchable @ECField(index=130)
@ECSearchable @ECField(index=140)
@Size(max=100, message="err.timezone.length")
@Type(type=ENCRYPTED_STRING) @Column(columnDefinition="varchar("+(100+ENC_PAD)+") NOT NULL")
@Getter @Setter private String timezone = "America/New_York";

@ECSearchable @ECField(index=140)
@ECSearchable @ECField(index=150)
@Column(nullable=false)
@ECIndex @Getter @Setter private Boolean syncPassword;
public boolean syncPassword() { return bool(syncPassword); }

@ECSearchable @ECField(index=150)
@ECSearchable @ECField(index=160)
@Column(nullable=false)
@ECIndex @Getter @Setter private Boolean launchLock;
public boolean launchLock() { return bool(launchLock); }

@ECSearchable @ECField(index=160)
@ECSearchable @ECField(index=170)
@Column(nullable=false)
@ECIndex @Getter @Setter private Boolean sendErrors;
public boolean sendErrors() { return bool(sendErrors); }

@ECSearchable @ECField(index=170)
@ECSearchable @ECField(index=180)
@Column(nullable=false)
@ECIndex @Getter @Setter private Boolean sendMetrics;
public boolean sendMetrics() { return bool(sendMetrics); }
@@ -190,7 +196,7 @@ public class BubbleNetwork extends IdentifiableBase implements HasNetwork, HasBu
public boolean hasForkHost () { return !empty(forkHost); }
public boolean fork() { return hasForkHost(); }

@ECSearchable @ECField(index=160)
@ECSearchable @ECField(index=190)
@Column(length=20)
@Enumerated(EnumType.STRING) @Getter @Setter private BubbleNetworkState state = created;



+ 1
- 1
bubble-server/src/main/java/bubble/resources/account/AuthResource.java 파일 보기

@@ -214,7 +214,7 @@ public class AuthResource {
final ValidationResult errors = request.validateEmail();
if (errors.isValid()) {
final Account existing = accountDAO.findByEmail(request.getEmail());
if (existing != null) errors.addViolation("err.name.registered", "Name is already registered: ", request.getEmail());
if (existing != null) errors.addViolation("err.email.registered", "Email is already registered: ", request.getEmail());
}

final ConstraintViolationBean passwordViolation = validatePassword(request.getPassword());


+ 13
- 1
bubble-server/src/main/java/bubble/resources/bill/AccountPlansResource.java 파일 보기

@@ -45,6 +45,7 @@ import static bubble.model.cloud.BubbleNetwork.validateHostname;
import static org.cobbzilla.util.daemon.ZillaRuntime.empty;
import static org.cobbzilla.util.string.ValidationRegexes.HOST_PATTERN;
import static org.cobbzilla.util.string.ValidationRegexes.validateRegexMatches;
import static org.cobbzilla.wizard.model.NamedEntity.NAME_MAXLEN;
import static org.cobbzilla.wizard.resources.ResourceUtil.*;

@Slf4j
@@ -144,7 +145,18 @@ public class AccountPlansResource extends AccountOwnedResource<AccountPlan, Acco
}
}
} else {
validateName(request, errors);
if (!request.hasNickname()) {
if (request.hasName()) {
request.setNickname(request.getName());
} else {
errors.addViolation("err.name.required");
}
}
if (request.hasNickname() && request.getNickname().length() > NAME_MAXLEN) {
errors.addViolation("err.name.tooLong");
}
// assign a random name for the network
request.setName(newNetworkName());
}
log.info("setReferences: after calling validateName, request.name="+request.getName());



+ 4
- 5
bubble-server/src/main/java/bubble/server/BubbleConfiguration.java 파일 보기

@@ -38,10 +38,7 @@ import org.cobbzilla.wizard.cache.redis.HasRedisConfiguration;
import org.cobbzilla.wizard.cache.redis.RedisConfiguration;
import org.cobbzilla.wizard.client.ApiClientBase;
import org.cobbzilla.wizard.server.RestServerHarness;
import org.cobbzilla.wizard.server.config.HasDatabaseConfiguration;
import org.cobbzilla.wizard.server.config.LegalInfo;
import org.cobbzilla.wizard.server.config.PgRestServerConfiguration;
import org.cobbzilla.wizard.server.config.RecaptchaConfig;
import org.cobbzilla.wizard.server.config.*;
import org.cobbzilla.wizard.util.ClasspathScanner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -87,6 +84,7 @@ public class BubbleConfiguration extends PgRestServerConfiguration
public static final String TAG_SSL_PORT = "sslPort";
public static final String TAG_PROMO_CODE_POLICY = "promoCodePolicy";
public static final String TAG_REQUIRE_SEND_METRICS = "requireSendMetrics";
public static final String TAG_SUPPORT = "support";
public static final String TAG_RESTORE_MODE = "isInRestoringStatus";

public static final String DEFAULT_LOCAL_STORAGE_DIR = HOME_DIR + "/.bubble_local_storage";
@@ -296,7 +294,8 @@ public class BubbleConfiguration extends PgRestServerConfiguration
{TAG_LOCKED, accountDAO.locked()},
{TAG_LAUNCH_LOCK, isSageLauncher() || thisNetwork == null ? null : thisNetwork.launchLock()},
{TAG_RESTORE_MODE, thisNode.wasRestored()},
{TAG_SSL_PORT, getDefaultSslPort()}
{TAG_SSL_PORT, getDefaultSslPort()},
{TAG_SUPPORT, getSupport()}
}));
}
return publicSystemConfigs.get();


+ 7
- 2
bubble-server/src/main/java/bubble/service/account/StandardAccountMessageService.java 파일 보기

@@ -52,7 +52,8 @@ public class StandardAccountMessageService implements AccountMessageService {
final Account account = accountDAO.findByUuid(accountUuid);
AccountPolicy policy = policyDAO.findSingleByAccount(accountUuid);
if (policy == null) {
policy = policyDAO.create(new AccountPolicy().setAccount(accountUuid));
log.warn("send("+message+"): no policy for account");
return false;
}
final List<AccountContact> contacts = policy.getAllowedContacts(message);
if (contacts.isEmpty()) {
@@ -136,9 +137,11 @@ public class StandardAccountMessageService implements AccountMessageService {
if (account == null) account = accountDAO.findByUuid(approval.getAccount());
final AccountMessageApprovalStatus approvalStatus = messageDAO.requestApproved(account, approval, token, data);
if (approvalStatus == AccountMessageApprovalStatus.ok_confirmed) {
final AccountMessage request = messageDAO.findOperationRequest(approval);
if (request == null) throw invalidEx("err.approvalToken.invalid", "Request could not be found for approval: "+approval);
final AccountPolicy policy = policyDAO.findSingleByAccount(account.getUuid());
final AccountMessage confirm = messageDAO.create(new AccountMessage(approval).setMessageType(AccountMessageType.confirmation));
approval.setRequest(messageDAO.findOperationRequest(approval));
approval.setRequest(request);
approval.setRequestContact(policy.findContactByUuid(approval.getRequest().getContact()));
getCompletionHandler(approval).confirm(approval, data);

@@ -158,6 +161,7 @@ public class StandardAccountMessageService implements AccountMessageService {
throw invalidEx("err.approvalToken.invalid", "Approval cannot proceed: "+approvalStatus, approvalStatus.name());
}

@Getter(lazy=true) private final AccountMessageCompletionHandler accountWelcomeHandler = configuration.autowire(new AccountVerifyHandler());
@Getter(lazy=true) private final AccountMessageCompletionHandler accountLoginHandler = configuration.autowire(new AccountLoginHandler());
@Getter(lazy=true) private final AccountMessageCompletionHandler accountPasswordHandler = configuration.autowire(new AccountPasswordHandler());
@Getter(lazy=true) private final AccountMessageCompletionHandler accountVerifyHandler = configuration.autowire(new AccountVerifyHandler());
@@ -170,6 +174,7 @@ public class StandardAccountMessageService implements AccountMessageService {
@Getter(lazy=true) private final Map<String, AccountMessageCompletionHandler> confirmationHandlers = initConfirmationHandlers();
private HashMap<String, AccountMessageCompletionHandler> initConfirmationHandlers() {
final HashMap<String, AccountMessageCompletionHandler> handlers = new HashMap<>();
handlers.put(ActionTarget.account+":"+AccountAction.welcome, getAccountWelcomeHandler());
handlers.put(ActionTarget.account+":"+AccountAction.login, getAccountLoginHandler());
handlers.put(ActionTarget.account+":"+AccountAction.password, getAccountPasswordHandler());
handlers.put(ActionTarget.account+":"+AccountAction.verify, getAccountVerifyHandler());


+ 1
- 1
bubble-server/src/main/resources/META-INF/bubble/bubble.properties 파일 보기

@@ -1 +1 @@
bubble.version=0.11.1
bubble.version=0.11.2

+ 4
- 0
bubble-server/src/main/resources/bubble-config.yml 파일 보기

@@ -71,6 +71,10 @@ errorApi:
key: {{ERRBIT_KEY}}
env: {{ERRBIT_ENV}}

support:
email: '{{SUPPORT_EMAIL}}'
site: '{{SUPPORT_SITE}}'

localNotificationStrategy: {{#exists BUBBLE_LOCAL_NOTIFY}}{{BUBBLE_LOCAL_NOTIFY}}{{else}}inline{{/exists}}

letsencryptEmail: {{LETSENCRYPT_EMAIL}}


+ 12245
- 0
bubble-server/src/main/resources/bubble/host-prefixes.txt
파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
파일 보기


+ 4
- 2
bubble-server/src/main/resources/message_templates/en_US/email/notice/welcome/account/message.hbs 파일 보기

@@ -2,8 +2,10 @@ Hello {{account.name}},

Welcome to Bubble!

You can login with your username and password here: {{publicUri}}/login
Please confirm your email address using this link:

If you have any questions, please contact your Bubble Administrator.
{{publicUri}}/me/action?approve={{confirmationToken}}

{{#if configuration.hasSupportInfo}}If you have any questions or need help, please {{#if configuration.support.hasEmailAndSite}}contact {{configuration.support.email}} or visit {{configuration.support.site}}{{else}}{{#if configuration.support.hasEmail}}contact {{configuration.support.email}}{{else}}visit {{configuration.support.site}}{{/if}}{{/if}}{{/if}}

Happy bubbling!

+ 1
- 9
bubble-server/src/main/resources/message_templates/en_US/email/request/verify/account/message.hbs 파일 보기

@@ -3,24 +3,10 @@ Hello {{account.name}},
Contact information has been added to your account named '{{account.name}}' on {{network.networkDomain}}

{{#string_compare contact.uuid '==' message.contact}}{{contact.type}} - {{contact.info}}{{else}}{{message.requestContact.type}}{{#if message.requestContact.isSms}}{{message.requestContact.info}}{{/if}}{{/string_compare}} {{#if message.requestContact.nick}}({{message.requestContact.nick}}){{/if}}


If you did not make this request or would like to cancel this request, please click this link:

{{publicUri}}/me/action?deny={{confirmationToken}}

{{#string_compare contact.uuid '==' message.contact}}
If you DID make this request and are ready to verify this contact information, click the link below,
or enter the value {{confirmationToken}} when the verification code is requested.
To confirm this contact information, follow this link:

{{publicUri}}/me/action?approve={{confirmationToken}}

{{/string_compare}}

Thank you for using Bubble!

+ 3
- 3
bubble-server/src/main/resources/message_templates/en_US/server/post_auth/ResourceMessages.properties 파일 보기

@@ -184,8 +184,8 @@ label_field_nodes_ip6=IPv6
# New Network page
message_no_contacts=No authorized contact info found
link_label_no_contacts=Add an email address or SMS-enabled phone number
message_no_verified_contacts=No verified contact info found
message_no_verified_contacts_subtext=Before creating your first Bubble, please verify the contact information shown below
message_no_verified_contacts=Please verify your email address
message_no_verified_contacts_subtext=Check your email and follow the link to verify your email address

form_title_new_network=New Bubble
field_label_network_name=Name
@@ -742,7 +742,7 @@ err.tgzB64.invalid.missingTasksMainYml=No tasks/main.yml file found for role in
err.tgzB64.invalid.writingToStorage=Error writing tgz to storage
err.tgzB64.invalid.readingFromStorage=Error reading tgz from storage
err.tgzB64.required=tgzB64 is required
err.totpKey.length=TOTP key is required
err.totpKey.length=TOTP key is too long
err.type.notVerifiable=Type is not verifiable
err.type.invalid=Type is invalid
err.type.required=Type is required


+ 8
- 0
bubble-server/src/main/resources/message_templates/en_US/server/pre_auth/ResourceMessages.properties 파일 보기

@@ -12,6 +12,13 @@ message_undefined=undefined
# Display of percent values has localized variations
label_percent={{percent}}%

# Support
title_support=Bubble Support
support_preamble=To get help with Bubble:
support_site_link=Visit our Support Website
support_email_link=Send us an email
support_not_available=Sorry, no support options are available

# Legal page links
title_legal_topics=Legal Stuff
legal_topics=terms,privacy,source,license,3rdParty_licenses
@@ -264,6 +271,7 @@ form_title_login=Login
form_title_restore=Restore
field_label_username=Username
field_label_password=Password
field_label_password_guidance=Password must be at least 8 characters long.<br/>Password must contain at least one letter, one number, and one special character.
field_label_confirm_password=Confirm Password
field_label_unlock_key=Unlock Key
field_label_restore_short_key=Short Key (6 letters)


+ 1
- 1
bubble-server/src/main/resources/message_templates/en_US/sms/notice/welcome/account/message.hbs 파일 보기

@@ -1,2 +1,2 @@
{{network.networkDomain}}: Welcome to Bubble, {{account.name}}!
Login here: {{publicUri}}/login
Verify your email: {{publicUri}}/me/action?approve={{confirmationToken}}

+ 2
- 1
bubble-server/src/main/resources/message_templates/en_US/sms/request/verify/account/message.hbs 파일 보기

@@ -1 +1,2 @@
{{network.networkDomain}}: {{#string_compare contact.uuid '==' message.contact}}SMS Phone added: {{contact.info}} - To approve, use code {{confirmationToken}} or use: {{publicUri}}/me/action?approve={{confirmationToken}} - To deny: {{publicUri}}/me/action?deny={{confirmationToken}}{{else}}{{#if message.requestContact.isEmail}}Email added: {{message.requestContact.info}}{{else}}Auth added: {{message.requestContact.type}}{{/if}} - To deny: {{publicUri}}/me/action?deny={{confirmationToken}}{{/string_compare}}
{{network.networkDomain}}: {{#string_compare contact.uuid '==' message.contact}}SMS Phone added: {{contact.info}}
To approve: {{publicUri}}/me/action?approve={{confirmationToken}}{{else}}{{#if message.requestContact.isEmail}}Email added: {{message.requestContact.info}}{{else}}Auth added: {{message.requestContact.type}}{{/if}}{{/string_compare}}

+ 2
- 2
bubble-server/src/test/resources/models/tests/auth/account_registration.json 파일 보기

@@ -195,7 +195,7 @@
},

{
"comment": "new session, register new user, fails because username is already used",
"comment": "new session, register new user, fails because email is already used",
"request": {
"session": "new",
"uri": "auth/register",
@@ -207,7 +207,7 @@
},
"response": {
"status": 422,
"check": [ {"condition": "json.has('err.name.registered')"} ]
"check": [ {"condition": "json.has('err.email.registered')"} ]
}
},



+ 80
- 32
bubble-server/src/test/resources/models/tests/auth/multifactor_auth.json 파일 보기

@@ -3,7 +3,55 @@
"comment": "create new account and login",
"include": "new_account",
"params": {
"email": "user-multifactor_auth@example.com"
"email": "user-multifactor_auth_registered@example.com"
}
},

{
"comment": "resend verification message for registration email",
"request": {
"uri": "users/{{userAccount.name}}/policy/contacts/verify",
"entity": {
"type": "email",
"info": "user-multifactor_auth_registered@example.com"
}
}
},

{
"before": "sleep 1s",
"comment": "as root, check inbox for email verification message for registration email",
"request": {
"session": "rootSession",
"uri": "debug/inbox/email/user-multifactor_auth_registered@example.com?action=verify"
},
"response": {
"store": "userInbox",
"check": [
{"condition": "'{{json.[0].ctx.message.messageType}}' == 'request'"},
{"condition": "'{{json.[0].ctx.message.action}}' == 'verify'"},
{"condition": "'{{json.[0].ctx.message.target}}' == 'account'"}
]
}
},

{
"comment": "as user, approve email verification request for registration email",
"request": {
"session": "userSession",
"uri": "auth/approve/{{userInbox.[0].ctx.confirmationToken}}",
"entity": [{"name": "account", "value": "user-multifactor_auth_registered@example.com"}]
}
},

{
"comment": "add second email to account policy",
"request": {
"uri": "users/{{userAccount.name}}/policy/contacts",
"entity": {
"type": "email",
"info": "user-multifactor_auth@example.com"
}
}
},

@@ -14,9 +62,9 @@
"store": "policy",
"check": [
{"condition": "json.getAccountContacts() != null"},
{"condition": "json.getAccountContacts().length == 1"},
{"condition": "!json.getAccountContacts()[0].authFactor()"},
{"condition": "!json.getAccountContacts()[0].verified()"}
{"condition": "json.getAccountContacts().length == 2"},
{"condition": "!json.getAccountContacts()[1].authFactor()"},
{"condition": "!json.getAccountContacts()[1].verified()"}
]
}
},
@@ -72,7 +120,7 @@
"request": {
"session": "userSession",
"uri": "auth/approve/{{userInbox.[0].ctx.confirmationToken}}",
"entity": [{"name": "account", "value": "user-multifactor_auth@example.com"}]
"entity": [{"name": "account", "value": "user-multifactor_auth_registered@example.com"}]
}
},

@@ -84,9 +132,9 @@
"store": "policy",
"check": [
{"condition": "json.getAccountContacts() != null"},
{"condition": "json.getAccountContacts().length == 1"},
{"condition": "!json.getAccountContacts()[0].authFactor()"},
{"condition": "json.getAccountContacts()[0].verified()"}
{"condition": "json.getAccountContacts().length == 2"},
{"condition": "!json.getAccountContacts()[1].authFactor()"},
{"condition": "json.getAccountContacts()[1].verified()"}
]
}
},
@@ -117,8 +165,8 @@
"store": "policy",
"check": [
{"condition": "json.getAccountContacts() != null"},
{"condition": "json.getAccountContacts().length == 1"},
{"condition": "json.getAccountContacts()[0].requiredAuthFactor()"}
{"condition": "json.getAccountContacts().length == 2"},
{"condition": "json.getAccountContacts()[1].requiredAuthFactor()"}
]
}
},
@@ -170,7 +218,7 @@
"request": {
"session": "userSession",
"uri": "auth/approve/{{userInbox.[0].ctx.confirmationToken}}",
"entity": [{"name": "account", "value": "user-multifactor_auth@example.com"}]
"entity": [{"name": "account", "value": "user-multifactor_auth_registered@example.com"}]
},
"response": {
"sessionName": "userSession",
@@ -194,9 +242,9 @@
"store": "policy",
"check": [
{"condition": "json.getAccountContacts() != null"},
{"condition": "json.getAccountContacts().length == 2"},
{"condition": "json.getAccountContacts()[0].requiredAuthFactor()"},
{"condition": "json.getAccountContacts()[1].requiredAuthFactor()"}
{"condition": "json.getAccountContacts().length == 3"},
{"condition": "json.getAccountContacts()[1].requiredAuthFactor()"},
{"condition": "json.getAccountContacts()[2].requiredAuthFactor()"}
]
}
},
@@ -248,7 +296,7 @@
"request": {
"session": "userSession",
"uri": "auth/approve/{{userInbox.[0].ctx.confirmationToken}}",
"entity": [{"name": "account", "value": "user-multifactor_auth@example.com"}]
"entity": [{"name": "account", "value": "user-multifactor_auth_registered@example.com"}]
},
"response": {
"status": 422,
@@ -262,7 +310,7 @@
"session": "userSession",
"uri": "auth/approve/{{userInbox.[0].ctx.confirmationToken}}",
"entity": [
{"name": "account", "value": "user-multifactor_auth@example.com"},
{"name": "account", "value": "user-multifactor_auth_registered@example.com"},
{"name": "totpToken", "value": "{{authenticator_token authenticator.totpKey}}"}
]
},
@@ -308,8 +356,8 @@
"store": "policy",
"check": [
{"condition": "json.getAccountContacts() != null"},
{"condition": "json.getAccountContacts().length == 1"},
{"condition": "json.getAccountContacts()[0].requiredAuthFactor()"}
{"condition": "json.getAccountContacts().length == 2"},
{"condition": "json.getAccountContacts()[1].requiredAuthFactor()"}
]
}
},
@@ -372,7 +420,7 @@
"request": {
"session": "userSession",
"uri": "auth/approve/{{smsInbox.[0].ctx.confirmationToken}}",
"entity": [{"name": "account", "value": "user-multifactor_auth@example.com"}]
"entity": [{"name": "account", "value": "user-multifactor_auth_registered@example.com"}]
}
},

@@ -384,10 +432,10 @@
"store": "policy",
"check": [
{"condition": "json.getAccountContacts() != null"},
{"condition": "json.getAccountContacts().length == 2"},
{"condition": "json.getAccountContacts()[1].getType().name() == 'sms'"},
{"condition": "!json.getAccountContacts()[1].authFactor()"},
{"condition": "json.getAccountContacts()[1].verified()"}
{"condition": "json.getAccountContacts().length == 3"},
{"condition": "json.getAccountContacts()[2].getType().name() == 'sms'"},
{"condition": "!json.getAccountContacts()[2].authFactor()"},
{"condition": "json.getAccountContacts()[2].verified()"}
]
}
},
@@ -459,7 +507,7 @@
"request": {
"session": "userSession",
"uri": "auth/approve/{{smsInbox.[0].ctx.confirmationToken}}",
"entity": [{"name": "account", "value": "user-multifactor_auth@example.com"}]
"entity": [{"name": "account", "value": "user-multifactor_auth_registered@example.com"}]
},
"response": {
"status": 422,
@@ -473,7 +521,7 @@
"session": "userSession",
"uri": "auth/approve/{{smsInbox.[0].ctx.confirmationToken}}",
"entity": [
{"name": "account", "value": "user-multifactor_auth@example.com"},
{"name": "account", "value": "user-multifactor_auth_registered@example.com"},
{"name": "totpToken", "value": "{{authenticator_token authenticator.totpKey}}"}
]
},
@@ -505,8 +553,8 @@
"store": "policy",
"check": [
{"condition": "json.getAccountContacts() != null"},
{"condition": "json.getAccountContacts().length == 1"},
{"condition": "json.getAccountContacts()[0].getType().name() == 'sms'"}
{"condition": "json.getAccountContacts().length == 2"},
{"condition": "json.getAccountContacts()[1].getType().name() == 'sms'"}
]
}
},
@@ -550,7 +598,7 @@
"request": {
"session": "userSession",
"uri": "auth/approve/{{userInbox.[0].ctx.confirmationToken}}",
"entity": [{"name": "account", "value": "user-multifactor_auth@example.com"}]
"entity": [{"name": "account", "value": "user-multifactor_auth_registered@example.com"}]
}
},

@@ -619,7 +667,7 @@
"request": {
"session": "userSession",
"uri": "auth/approve/{{userInbox.[0].ctx.confirmationToken}}",
"entity": [{"name": "account", "value": "user-multifactor_auth@example.com"}]
"entity": [{"name": "account", "value": "user-multifactor_auth_registered@example.com"}]
},
"response": {
"sessionName": "userSession",
@@ -736,9 +784,9 @@
"store": "policy",
"check": [
{"condition": "json.getAccountContacts() != null"},
{"condition": "json.getAccountContacts().length == 2"},
{"condition": "!json.getAccountContacts()[0].getInfo().startsWith('bar')"},
{"condition": "!json.getAccountContacts()[1].getInfo().startsWith('bar')"}
{"condition": "json.getAccountContacts().length == 3"},
{"condition": "!json.getAccountContacts()[1].getInfo().startsWith('bar')"},
{"condition": "!json.getAccountContacts()[2].getInfo().startsWith('bar')"}
]
}
}

+ 1
- 1
bubble-web

@@ -1 +1 @@
Subproject commit 62a5a97abc1e28ba23874394eef34ff03e828a1f
Subproject commit 9158e9660c4382363713b115ddb46637af99558e

+ 1
- 1
utils/cobbzilla-utils

@@ -1 +1 @@
Subproject commit b1273943835c8002c7aae24b880f2038fa71e73c
Subproject commit e5d7abc4b58a339a5da90fcfe53ba21c20e40c75

+ 1
- 1
utils/cobbzilla-wizard

@@ -1 +1 @@
Subproject commit 3b1649f05991edaf82d117ecf3080e46a16b63b4
Subproject commit c8c831c10c9d31957cc99f72662530bcb38f9af8

불러오는 중...
취소
저장