1

I use reCAPTCHA in my Java application and there is an approach similar to the following code:

LoginRequest:

public class LoginRequest {
    @NotEmpty
    private String token;
    
    @NotEmpty
    private String username;
    @NotEmpty
    private String password;
}

LoginController :




public class LoginController {

    private final Validator validator;

    @Value("${isDevelopment}")
    private boolean isDevelopment;

    public LoginController(Validator validator) {
        this.validator = validator;
    }

    public User login(LoginRequest request) {
        if(isDevelopment || validator.isValid(request.token)) {
            // ...
        }
        throw new InvalidTokenException();
    }
}

In this approach, I thought to bypass validation using the application development environment variable isDevelopment. However, I am not sure if there is a more elegant way. Because reCAPTCHA is used widely and most probably there would be a better standards for this purpose. How can I bypass reCAPTCHA during development?

Update: Here is my Validator class:

@Component
@RequiredArgsConstructor
public class CaptchaValidator {

    private final RecaptchaEnterpriseServiceClient client;

    @Value("${enabled}")
    private boolean isEnabled;

    public boolean isValid(String token) {
      // code omitted
    }
}

Jack
  • 1
  • 21
  • 118
  • 236

1 Answers1

2

Technically, such design is not wrong - this kind of approach is being used widely. It is named Feature Flags and there are 3rd party software solutions that help developers to work with such variables (like e.g. LaunchDarkly).

From Objective Programming perspective, you can also deal with this situation by creating some wrapper interface (let say UserValidator), injecting this to LoginController...

public class LoginController {
    private final UserValidator validator;

    public LoginController(UserValidator validator) {
        this.validator = validator;
    }

    // ... 

    public User login(LoginRequest request) {
        if(validator.validate(request.token)) { 
            // ...

...making your CaptchaValidator implementing this but also creating some dev stub for the interface (let say AlwaysTrueValidator). Then during the development you can use your stub and just change configuration when going live. You can even take some advantage of using Spring profiles.

@Configuration
@Profile("production")
public class ProductionConfiguration {
    @Bean
    public LoginController loginController() {
        return new LoginController(new CaptchaValidator());
    }
}

// ...

@Configuration
@Profile("development")
public class DevelopmentConfiguration {
    @Bean
    public LoginController loginController() {
        return new LoginController(new AlwaysTrueValidator());
    }
}
m.antkowicz
  • 13,268
  • 18
  • 37
  • Thanks you very much for this wonderful explanations. Actually Spring profiles seems good and professional, but as far as I see, the other approach (using an environment variable in application.yml) is also good for me. – Jack Jul 13 '21 at 10:07
  • I have a **question** regarding to the `wrapper interface`. I use a class called `UserValidator` and I already inject it to my `LoginController` (I updated omitted part, please see `LoginController`. At this stage, should I convert this `UserValidator` class to an interface, or can I continue to use it as class. What would you suggest? – Jack Jul 13 '21 at 10:08
  • I would say that in Java choosing interface should be always default choice. Because your case is really aligned for interfaces I would transform this class to the interface. – m.antkowicz Jul 13 '21 at 10:20
  • Sorry, but I am not sure how can I convert it to interface properly. I added myValidator class as Update in my question. Could you pls convert it to interface? I think I create a `Validator` interface and its implementation called `ValidatorImpl`, but cannot implement them properly. Any example for class and its interface? – Jack Jul 13 '21 at 11:07
  • Actually what you've posted is not `Validator` class but implementation actually (even if the `isValid` method is empty) :) I would suggest to create just interface `public interface Validator` with one method `boolean isValid();` (here you could have even default implementation returning `true` btw) and then create your `CaptchaValidator` that will `implements Validator`. You can still have there Lombok annotation but remove this `@Component` and create Java spring configuration class with method `@Bean public Validator validator() { return new CaptchaValidator(...); }` – m.antkowicz Jul 14 '21 at 11:52
  • Thansk you very much for your good explanations. Could you please post them as answer by providing an example Validator interface and CaptchaValidator class as implementation? – Jack Jul 14 '21 at 12:26