mirror of https://github.com/halo-dev/halo
				
				
				
			Add support to disable two-factor authentication (#6242)
#### 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
支持通过配置的方式全局禁用二步验证
```
			
			
				pull/6279/head
			
			
		
							parent
							
								
									0b7b74e826
								
							
						
					
					
						commit
						cc3564bf82
					
				| 
						 | 
				
			
			@ -127,8 +127,12 @@ public class WebServerSecurityConfig {
 | 
			
		|||
 | 
			
		||||
    @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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 {
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,6 +7,7 @@ import static run.halo.app.security.authorization.AuthorityUtils.ANONYMOUS_ROLE_
 | 
			
		|||
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;
 | 
			
		||||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
| 
						 | 
				
			
			@ -66,7 +73,9 @@ public class DefaultUserDetailService
 | 
			
		|||
                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();
 | 
			
		||||
                }));
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -156,11 +156,27 @@ class DefaultUserDetailServiceTest {
 | 
			
		|||
            .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");
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue