Skip to content

Commit

Permalink
Add support to disable two-factor authentication (#6242)
Browse files Browse the repository at this point in the history
#### What type of PR is this?

/kind improvement
/area core
/milestone 2.17.0

#### What this PR does / why we need it:

This PR provides a configuration property to control whether two-factor authentication is disabled. e.g.:

```yaml
halo:
  security:
    two-factor-auth:
      disabled: true | false # Default is false.
```

#### Which issue(s) this PR fixes:

Fixes #5640 

#### Special notes for your reviewer:

1. Enable 2FA and configure TOTP
2. Disable 2FA by configuring property above
3. Restart Halo and try to login

#### Does this PR introduce a user-facing change?

```release-note
支持通过配置的方式全局禁用二步验证
```
  • Loading branch information
JohnNiang authored Jul 1, 2024
1 parent 0b7b74e commit cc3564b
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,12 @@ public ReactiveIndexedSessionRepository<MapSession> reactiveSessionRepository(

@Bean
DefaultUserDetailService userDetailsService(UserService userService,
RoleService roleService) {
return new DefaultUserDetailService(userService, roleService);
RoleService roleService,
HaloProperties haloProperties) {
var userDetailService = new DefaultUserDetailService(userService, roleService);
var twoFactorAuthDisabled = haloProperties.getSecurity().getTwoFactorAuth().isDisabled();
userDetailService.setTwoFactorAuthDisabled(twoFactorAuthDisabled);
return userDetailService;
}

@Bean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,18 @@ public class SecurityProperties {

private final RememberMeOptions rememberMe = new RememberMeOptions();

private final TwoFactorAuthOptions twoFactorAuth = new TwoFactorAuthOptions();

@Data
public static class TwoFactorAuthOptions {

/**
* Whether two-factor authentication is disabled.
*/
private boolean disabled;

}

@Data
public static class FrameOptions {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import static run.halo.app.security.authorization.AuthorityUtils.AUTHENTICATED_ROLE_NAME;
import static run.halo.app.security.authorization.AuthorityUtils.ROLE_PREFIX;

import lombok.Setter;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.ReactiveUserDetailsPasswordService;
Expand All @@ -31,6 +32,12 @@ public class DefaultUserDetailService

private final RoleService roleService;

/**
* Indicates whether two-factor authentication is disabled.
*/
@Setter
private boolean twoFactorAuthDisabled;

public DefaultUserDetailService(UserService userService, RoleService roleService) {
this.userService = userService;
this.roleService = roleService;
Expand Down Expand Up @@ -66,7 +73,9 @@ public Mono<UserDetails> findByUsername(String username) {
return setAuthorities.then(Mono.fromSupplier(() -> {
var twoFactorAuthSettings = TwoFactorUtils.getTwoFactorAuthSettings(user);
return new HaloUser.Builder(userBuilder.build())
.twoFactorAuthEnabled(twoFactorAuthSettings.isAvailable())
.twoFactorAuthEnabled(
(!twoFactorAuthDisabled) && twoFactorAuthSettings.isAvailable()
)
.totpEncryptedSecret(user.getSpec().getTotpEncryptedSecret())
.build();
}));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,11 +156,27 @@ void shouldFindHaloUserDetailsWith2faEnabledWhen2faEnabledAndTotpConfigured() {
.verifyComplete();
}

@Test
void shouldFindHaloUserDetailsWith2faDisabledWhen2faDisabledGlobally() {
userDetailService.setTwoFactorAuthDisabled(true);
var fakeUser = createFakeUser();
fakeUser.getSpec().setTwoFactorAuthEnabled(true);
fakeUser.getSpec().setTotpEncryptedSecret("fake-totp-encrypted-secret");
when(userService.getUser("faker")).thenReturn(Mono.just(fakeUser));
when(roleService.listRoleRefs(any())).thenReturn(Flux.empty());
userDetailService.findByUsername("faker")
.as(StepVerifier::create)
.assertNext(userDetails -> {
assertInstanceOf(HaloUserDetails.class, userDetails);
assertFalse(((HaloUserDetails) userDetails).isTwoFactorAuthEnabled());
})
.verifyComplete();
}

@Test
void shouldFindUserDetailsByExistingUsernameButKindOfRoleRefIsNotRole() {
var foundUser = createFakeUser();

var roleGvk = new Role().groupVersionKind();
var roleRef = new RoleRef();
roleRef.setKind("FakeRole");
roleRef.setApiGroup("fake.halo.run");
Expand Down

0 comments on commit cc3564b

Please sign in to comment.