Kaynağa Gözat

support authenticator config in ui

tags/v0.1.7^0
Jonathan Cobb 5 yıl önce
ebeveyn
işleme
f32da981e0
8 değiştirilmiş dosya ile 65 ekleme ve 8 silme
  1. +6
    -1
      bubble-server/src/main/java/bubble/model/account/AccountPolicy.java
  2. +2
    -0
      bubble-server/src/main/java/bubble/model/account/message/AccountMessageContact.java
  3. +13
    -3
      bubble-server/src/main/java/bubble/resources/account/AccountsResource.java
  4. +16
    -0
      bubble-server/src/main/java/bubble/resources/account/AuthResource.java
  5. +17
    -2
      bubble-server/src/main/java/bubble/service/AuthenticatorService.java
  6. +4
    -1
      bubble-server/src/main/resources/message_templates/en_US/server/post_auth/ResourceMessages.properties
  7. +6
    -0
      bubble-server/src/main/resources/message_templates/en_US/server/pre_auth/ResourceMessages.properties
  8. +1
    -1
      bubble-web

+ 6
- 1
bubble-server/src/main/java/bubble/model/account/AccountPolicy.java Dosyayı Görüntüle

@@ -46,7 +46,9 @@ public class AccountPolicy extends IdentifiableBase implements HasAccount {
public static final Long MIN_NODE_OPERATION_TIMEOUT = MINUTES.toMillis(1);
public static final Long MIN_AUTHENTICATOR_TIMEOUT = MINUTES.toMillis(1);

public static final String[] UPDATE_FIELDS = {"deletionPolicy", "nodeOperationTimeout", "accountOperationTimeout"};
public static final String[] UPDATE_FIELDS = {
"deletionPolicy", "nodeOperationTimeout", "accountOperationTimeout", "authenticatorTimeout"
};

public AccountPolicy(AccountPolicy policy) { copy(this, policy); }

@@ -70,6 +72,9 @@ public class AccountPolicy extends IdentifiableBase implements HasAccount {
@ECSearchable(type=EntityFieldType.time_duration) @ECField(index=40)
@Type(type=ENCRYPTED_LONG) @Column(columnDefinition="varchar("+ENC_LONG+") NOT NULL")
@Getter @Setter private Long authenticatorTimeout = MAX_AUTHENTICATOR_TIMEOUT;
public boolean authenticatorTimeoutChanged (AccountPolicy other) {
return authenticatorTimeout != null && other.getAuthenticatorTimeout() != null && !authenticatorTimeout.equals(other.getAuthenticatorTimeout());
}

@ECSearchable @ECField(index=50)
@Enumerated(EnumType.STRING) @Column(length=40, nullable=false)


+ 2
- 0
bubble-server/src/main/java/bubble/model/account/message/AccountMessageContact.java Dosyayı Görüntüle

@@ -15,6 +15,8 @@ public class AccountMessageContact implements Serializable {
@Getter @Setter private AccountMessage message;
@Getter @Setter private AccountContact contact;

public boolean valid () { return message != null && message.hasUuid() && contact != null && contact.hasUuid(); }

public String key() { return message.getUuid()+":"+contact.getUuid(); }

}

+ 13
- 3
bubble-server/src/main/java/bubble/resources/account/AccountsResource.java Dosyayı Görüntüle

@@ -139,6 +139,7 @@ public class AccountsResource {
@PathParam("id") String id) {
final AccountContext c = new AccountContext(ctx, id);
final AccountPolicy policy = policyDAO.findSingleByAccount(c.account.getUuid());
authenticatorService.ensureAuthenticated(ctx, policy, ActionTarget.account);
return policy == null ? notFound(id) : ok(policy.mask());
}

@@ -152,8 +153,12 @@ public class AccountsResource {
policy = policyDAO.create(new AccountPolicy(request).setAccount(c.account.getUuid()));
} else {
authenticatorService.ensureAuthenticated(ctx, policy, ActionTarget.account);
if (policy.authenticatorTimeoutChanged(request)) {
authenticatorService.updateExpiration(ctx, policy);
}
policy = policyDAO.update((AccountPolicy) policy.update(request));
}

return ok(policy.mask());
}

@@ -184,7 +189,7 @@ public class AccountsResource {
log.info("setContact: contact is new, sending verify message");
messageDAO.sendVerifyRequest(getRemoteHost(req), c.account, contact);
}
return ok(added);
return ok(added.mask());
}

@POST @Path("/{id}"+EP_POLICY+EP_CONTACTS+"/verify")
@@ -201,7 +206,7 @@ public class AccountsResource {
final AccountContact found = policy.findContact(contact);
if (found == null) return notFound(contact.getType()+"/"+contact.getInfo());
messageDAO.sendVerifyRequest(getRemoteHost(req), c.account, found);
return ok(policy);
return ok(policy.mask());
}

@GET @Path("/{id}"+EP_POLICY+EP_CONTACTS+"/{type}/{info}")
@@ -211,6 +216,7 @@ public class AccountsResource {
@PathParam("info") String info) {
final AccountContext c = new AccountContext(ctx, id);
final AccountPolicy policy = policyDAO.findSingleByAccount(c.account.getUuid());
authenticatorService.ensureAuthenticated(ctx, policy, ActionTarget.account);
final AccountContact contact = policy.findContact(new AccountContact().setType(type).setInfo(info));
return contact != null ? ok(contact.mask()) : notFound(type+"/"+info);
}
@@ -239,7 +245,11 @@ public class AccountsResource {

final AccountContact contact = policy.findContact(new AccountContact().setType(CloudServiceType.authenticator));
if (contact == null) return notFound(CloudServiceType.authenticator.name());
return ok(policyDAO.update(policy.removeContact(contact)).mask());

final AccountPolicy updated = policyDAO.update(policy.removeContact(contact)).mask();
authenticatorService.flush(c.caller.getToken());

return ok(updated);
}

@DELETE @Path("/{id}"+EP_POLICY+EP_CONTACTS+"/{uuid}")


+ 16
- 0
bubble-server/src/main/java/bubble/resources/account/AuthResource.java Dosyayı Görüntüle

@@ -322,9 +322,25 @@ public class AuthResource {
policyDAO.update(policy.verifyContact(policy.getAuthenticator().getUuid()));
return ok_empty();
}

final AccountMessage loginRequest = accountMessageDAO.findMostRecentLoginRequest(account.getUuid());
if (loginRequest == null) {
log.warn("authenticator: AccountMessage (loginRequest) was null, returning OK without doing anything further");
return ok_empty();
}

final AccountMessageContact amc = messageService.accountMessageContact(loginRequest, authenticator);
if (amc == null || !amc.valid()) {
log.warn("authenticator: AccountMessageContact was null or invalid, returning OK without doing anything further");
return ok_empty();
}

final AccountMessage approval = messageService.approve(account, getRemoteHost(req), amc.key());
if (approval == null) {
log.warn("authenticator: AccountMessage (approval) was null, returning OK without doing anything further");
return ok_empty();
}

if (approval.getMessageType() == AccountMessageType.confirmation) {
// OK we can log in!
return ok(account.setToken(sessionToken));


+ 17
- 2
bubble-server/src/main/java/bubble/service/AuthenticatorService.java Dosyayı Görüntüle

@@ -38,7 +38,7 @@ public class AuthenticatorService {
if (G_AUTH.authorize(secret, code)) {
final String sessionToken = request.startSession() ? sessionDAO.create(account) : account.getToken();
if (sessionToken == null) throw invalidEx("err.totpToken.noSession");
getAuthenticatorTimes().set(sessionToken, String.valueOf(now()), EX, policy.getAuthenticatorTimeout()/1000);
markAsAuthenticated(sessionToken, policy);
return sessionToken;

} else {
@@ -46,6 +46,10 @@ public class AuthenticatorService {
}
}

public void markAsAuthenticated(String sessionToken, AccountPolicy policy) {
getAuthenticatorTimes().set(sessionToken, String.valueOf(now()), EX, policy.getAuthenticatorTimeout()/1000);
}

public boolean isAuthenticated (String sessionToken) { return getAuthenticatorTimes().get(sessionToken) != null; }

public void ensureAuthenticated(ContainerRequest ctx) { ensureAuthenticated(ctx, null); }
@@ -74,6 +78,17 @@ public class AuthenticatorService {
if (!isAuthenticated(account.getToken())) throw invalidEx("err.totpToken.invalid");
}

public void flush(String sessionToken) { getAuthenticatorTimes().del(sessionToken); }
public boolean flush(String sessionToken) {
final String exists = getAuthenticatorTimes().get(sessionToken);
getAuthenticatorTimes().del(sessionToken);
return exists != null;
}

public void updateExpiration(ContainerRequest ctx, AccountPolicy policy) {
final Account account = userPrincipal(ctx);
final String sessionToken = account.getToken();
if (flush(sessionToken)) {
markAsAuthenticated(sessionToken, policy);
}
}
}

+ 4
- 1
bubble-server/src/main/resources/message_templates/en_US/server/post_auth/ResourceMessages.properties Dosyayı Görüntüle

@@ -97,6 +97,8 @@ field_label_policy_account_operation_timeout=Account Operation Timeout
field_label_policy_account_operation_timeout_description=To ensure your Account security, certain operations (like downloading your Account data) require your approval. If no response is received before this timeout, the operation will not be allowed to proceed.
field_label_policy_node_operation_timeout=Bubble Operation Timeout
field_label_policy_node_operation_timeout_description=To ensure your Bubble security, certain operations (like stopping your Bubble) require your approval. If no response is received before this timeout, the operation will not be allowed to proceed.
field_label_policy_authenticator_timeout=Authenticator Timeout
field_label_policy_authenticator_timeout_description=After successfully verifying your account with your Authenticator app, you will be required to authenticate again after this amount of time has passed.
button_label_update_policy=Update

# Policy Contact fields
@@ -367,7 +369,8 @@ err.cloudType.invalid=Cloud type is invalid
err.configJson.length=Configuration JSON is too long
err.contact.required=Contact information is required
err.contactType.required=Contact type is required
err.contact.unverified=Cannot set auth factor on an unverified contact; verify first
err.contact.authenticatorAuthFactorCannotBeChanged=Authenticator is always a required auth factor
err.contact.unverified=Cannot configure an unverified contact; verify first
err.country.invalid=Country is invalid, use a valid ISO 2-letter code
err.credentialsJson.length=Credentials JSON is too long
err.data.length=Data is too long


+ 6
- 0
bubble-server/src/main/resources/message_templates/en_US/server/pre_auth/ResourceMessages.properties Dosyayı Görüntüle

@@ -194,6 +194,12 @@ field_label_policy_contact_verified=Verified
field_label_policy_contact_verify_code=Enter Verification Code
button_label_submit_verify_code=Verify

# TOTP modal
form_title_totp_modal=Authentication Required
field_description_totp_code=Open your authentication app and enter the code for this service
field_label_totp_code=Enter 6 digit code
button_label_submit_totp_code=Verify

# Low-level errors and activation errors
err.cloud.noSuchField=A cloud driver config field name is invalid
err.cloud.invalidFieldType=Cloud driver config field type invalid


+ 1
- 1
bubble-web

@@ -1 +1 @@
Subproject commit b87ba60f28f565f2e32709e73bacbf847ccef29c
Subproject commit 7fe91bd61d92d8b70115f2dd7c5fd24c6a7410b6

Yükleniyor…
İptal
Kaydet