Real Time Oops Concepts Questions and Answers
Questions
- Suppose you have different types of users (AdminUser, RegularUser, GuestUser). How would you implement this using inheritance?
- What are the advantages and disadvantages of using inheritance for user roles instead of using an enum?
- Can you explain how you would handle multiple authentication mechanisms (OAuth, database authentication) using inheritance?
- How would you design a LoginService interface that supports both JWT-based authentication and OAuth authentication?
- Can you implement a UserService where the authenticateUser() method behaves differently for Admin and Regular users?
- How does polymorphism help in implementing different authentication strategies in Spring Security?
- Why is it a good practice to define an AuthenticationProvider abstract class instead of writing authentication logic directly in a controller?
- How does Spring Security use abstraction to handle different types of authentication mechanisms?
- Can you create an abstract class for UserValidation that enforces validation rules for all user types?
- Task:
- Implement an abstract class AuthenticationProvider that defines a method validateCredentials().
- Create two subclasses:
- DatabaseAuthProvider (validates credentials using a database).
- OAuthAuthProvider (validates credentials using OAuth tokens).
- Implement a LoginService that uses polymorphism to select the authentication strategy dynamically.
Questions & Answers
- Suppose you have different types of users (AdminUser, RegularUser, GuestUser). How would you implement this using inheritance?
We can implement this using class inheritance, where a base class (User) defines common attributes and methods, and different user roles (AdminUser, RegularUser, GuestUser) extend from it.
Implementation:
// Parent class: User
abstract class User {
protected String username;
protected String email;
public User(String username, String email) {
this.username = username;
this.email = email;
}
public abstract void accessDashboard();
}
// AdminUser class with extra permissions
class AdminUser extends User {
public AdminUser(String username, String email) {
super(username, email);
}
@Override
public void accessDashboard() {
System.out.println(“Admin access granted. You can manage users and settings.”);
}
}
// RegularUser class with standard access
class RegularUser extends User {
public RegularUser(String username, String email) {
super(username, email);
}
@Override
public void accessDashboard() {
System.out.println(“Regular User access granted. You can view and edit your data.”);
}
}
// GuestUser with limited access
class GuestUser extends User {
public GuestUser(String username, String email) {
super(username, email);
}
@Override
public void accessDashboard() {
System.out.println(“Guest access granted. Limited access to resources.”);
}
}
// Usage Example
public class UserDemo {
public static void main(String[] args) {
User admin = new AdminUser(“Harshitha”, “admin@example.com”);
User regular = new RegularUser(“John”, “john@example.com”);
User guest = new GuestUser(“Guest”, “guest@example.com”);
admin.accessDashboard();
regular.accessDashboard();
guest.accessDashboard();
}
}
How this relates to your work at Intuit:
At Intuit, you worked with Spring Security & Role-Based Access Control (RBAC) to control access to different user roles. This inheritance model represents how different users in a system can have different levels of access.
- What are the advantages and disadvantages of using inheritance for user roles instead of using an enum?
Advantages of Using Inheritance for User Roles
- Flexibility – Each user type can have different behavior (e.g., AdminUser can manage users, while GuestUser has read-only access).
- Encapsulation – Each role contains its specific behavior while inheriting common properties.
- Polymorphism – We can process all users using the base class (User), allowing easy extensibility.
- Custom Methods – Each role can have unique functionalities (e.g., AdminUser can create reports).
Disadvantages of Using Inheritance
- Tight Coupling – If new user roles are needed, modifying the class hierarchy may be necessary.
- Code Duplication – Some features might be duplicated if they don’t fit neatly in a hierarchy.
- Less Efficient for Simple Roles – If roles only differ by a few attributes, an enum with properties may be more efficient.
🔥 Alternative Approach: Using Enum
If roles only affect data and do not require different behaviors, an enum-based approach might be simpler:
enum UserRole {
ADMIN, REGULAR, GUEST;
}
How this relates to your work at Hexaware:
At Hexaware Technologies, you worked with RBAC in Spring Security. Sometimes enums are used to define roles in databases (ADMIN, USER, GUEST), and sometimes OOP-based role hierarchies are used for more dynamic permissions.
- Can you explain how you would handle multiple authentication mechanisms (OAuth, database authentication) using inheritance?
To support multiple authentication mechanisms, we can define an abstract class Authenticator and implement different authentication strategies (OAuthAuthenticator, DatabaseAuthenticator).
Implementation:
// Abstract Authenticator class
abstract class Authenticator {
public abstract boolean authenticate(String username, String password);
}
// OAuth-based authentication
class OAuthAuthenticator extends Authenticator {
@Override
public boolean authenticate(String token, String unused) {
System.out.println(“Authenticating via OAuth with token: ” + token);
return token.equals(“validOAuthToken”); // Dummy validation
}
}
// Database-based authentication
class DatabaseAuthenticator extends Authenticator {
@Override
public boolean authenticate(String username, String password) {
System.out.println(“Authenticating via database for user: ” + username);
return “user”.equals(username) && “pass”.equals(password); // Dummy validation
}
}
// Usage Example
public class AuthenticationDemo {
public static void main(String[] args) {
Authenticator dbAuth = new DatabaseAuthenticator();
Authenticator oauthAuth = new OAuthAuthenticator();
System.out.println(dbAuth.authenticate(“user”, “pass”)); // true
System.out.println(oauthAuth.authenticate(“validOAuthToken”, “”)); // true
}
}
How this relates to your work at Intuit:
You worked with Spring Security & OAuth authentication mechanisms in Azure DevOps. This inheritance-based strategy mirrors the real-world authentication flow, where different mechanisms (DB, OAuth, JWT) exist.
- How would you design a LoginService interface that supports both JWT-based authentication and OAuth authentication?
To design a LoginService that supports JWT and OAuth authentication, we use an interface with multiple implementations.
Implementation:
// LoginService interface
interface LoginService {
boolean login(String credential, String passwordOrToken);
}
// JWT Authentication Service
class JWTLoginService implements LoginService {
@Override
public boolean login(String jwtToken, String unused) {
System.out.println(“Authenticating via JWT with token: ” + jwtToken);
return jwtToken.equals(“validJWTToken”); // Dummy validation
}
}
// OAuth Authentication Service
class OAuthLoginService implements LoginService {
@Override
public boolean login(String oauthToken, String unused) {
System.out.println(“Authenticating via OAuth with token: ” + oauthToken);
return oauthToken.equals(“validOAuthToken”); // Dummy validation
}
}
// Database Authentication Service
class DatabaseLoginService implements LoginService {
@Override
public boolean login(String username, String password) {
System.out.println(“Authenticating via database for user: ” + username);
return “user”.equals(username) && “pass”.equals(password); // Dummy validation
}
}
// Factory to choose authentication mechanism dynamically
class LoginServiceFactory {
public static LoginService getLoginService(String authType) {
switch (authType) {
case “JWT”: return new JWTLoginService();
case “OAuth”: return new OAuthLoginService();
case “DB”: return new DatabaseLoginService();
default: throw new IllegalArgumentException(“Invalid authentication type”);
}
}
}
// Usage Example
public class LoginDemo {
public static void main(String[] args) {
LoginService loginService = LoginServiceFactory.getLoginService(“JWT”);
System.out.println(loginService.login(“validJWTToken”, “”)); // true
}
}
How this relates to your work at Intuit:
- You implemented OAuth and database authentication in Azure DevOps pipelines.
- You designed secure authentication flows using Spring Security.
- The Factory Pattern dynamically selects the authentication method at runtime.
- Can you implement a UserService where the authenticateUser() method behaves differently for Admin and Regular users?
We can use polymorphism to define different authentication behaviors for AdminUser and RegularUser.
Implementation:
// Abstract class for User
abstract class User {
protected String username;
protected String password;
public User(String username, String password) {
this.username = username;
this.password = password;
}
public abstract boolean authenticateUser(String inputPassword);
}
// AdminUser class with strict authentication rules
class AdminUser extends User {
public AdminUser(String username, String password) {
super(username, password);
}
@Override
public boolean authenticateUser(String inputPassword) {
System.out.println(“Admin authentication with strong security check.”);
return this.password.equals(inputPassword) && inputPassword.length() >= 10;
}
}
// RegularUser class with standard authentication
class RegularUser extends User {
public RegularUser(String username, String password) {
super(username, password);
}
@Override
public boolean authenticateUser(String inputPassword) {
System.out.println(“Regular user authentication.”);
return this.password.equals(inputPassword);
}
}
// UserService class to authenticate users
class UserService {
public boolean authenticate(User user, String inputPassword) {
return user.authenticateUser(inputPassword);
}
}
// Usage Example
public class UserAuthDemo {
public static void main(String[] args) {
UserService userService = new UserService();
User admin = new AdminUser(“HarshithaAdmin”, “StrongPassword123”);
User regular = new RegularUser(“JohnDoe”, “mypassword”);
System.out.println(userService.authenticate(admin, “StrongPassword123”)); // true
System.out.println(userService.authenticate(regular, “mypassword”)); // true
System.out.println(userService.authenticate(admin, “weakpass”)); // false
}
}
How this relates to your work at Intuit:
- You designed secure authentication flows for Admin & Regular users using Spring Security & RBAC.
- Admin authentication typically requires stronger password policies than Regular users.
- How does polymorphism help in implementing different authentication strategies in Spring Security?
Polymorphism allows different authentication strategies to be implemented using a common interface or abstract class.
In Spring Security, authentication providers such as OAuth, JWT, and Database authentication can be implemented using a common interface (AuthenticationProvider).
Example of Polymorphism in Authentication:
// AuthenticationProvider Interface
interface AuthenticationProvider {
boolean authenticate(String credential);
}
// OAuth Authentication
class OAuthAuthentication implements AuthenticationProvider {
@Override
public boolean authenticate(String token) {
System.out.println(“Authenticating via OAuth.”);
return token.equals(“validOAuthToken”);
}
}
// JWT Authentication
class JWTAuthentication implements AuthenticationProvider {
@Override
public boolean authenticate(String jwtToken) {
System.out.println(“Authenticating via JWT.”);
return jwtToken.equals(“validJWTToken”);
}
}
// Database Authentication
class DatabaseAuthentication implements AuthenticationProvider {
@Override
public boolean authenticate(String usernamePassword) {
System.out.println(“Authenticating via Database.”);
return usernamePassword.equals(“user:password”);
}
}
// AuthenticationService using polymorphism
class AuthenticationService {
private AuthenticationProvider authenticationProvider;
public AuthenticationService(AuthenticationProvider provider) {
this.authenticationProvider = provider;
}
public boolean login(String credential) {
return authenticationProvider.authenticate(credential);
}
}
// Usage Example
public class AuthDemo {
public static void main(String[] args) {
AuthenticationService authService = new AuthenticationService(new OAuthAuthentication());
System.out.println(authService.login(“validOAuthToken”)); // true
}
}
How this relates to your work at Hexaware:
- Spring Security uses AuthenticationProvider interfaces to handle OAuth, JWT, and database authentication dynamically.
- This reduces tight coupling and allows new authentication methods to be added easily.
- Why is it a good practice to define an AuthenticationProvider abstract class instead of writing authentication logic directly in a controller?
Reasons why authentication logic should NOT be in the controller:
- Separation of Concerns – The controller should only handle HTTP requests, not business logic.
- Reusability – By defining an AuthenticationProvider, different authentication methods can be reused.
- Testability – Separating authentication into a service or provider makes it easier to write unit tests.
- Security – Controllers should not handle raw passwords or tokens directly.
Example of an AuthenticationProvider Abstract Class:
// Abstract Authentication Provider
abstract class AuthenticationProvider {
public abstract boolean authenticate(String credential);
}
// OAuth Authentication
class OAuthAuthProvider extends AuthenticationProvider {
@Override
public boolean authenticate(String token) {
System.out.println(“Authenticating via OAuth.”);
return token.equals(“validOAuthToken”);
}
}
// Controller that delegates authentication logic
@RestController
@RequestMapping(“/login”)
class AuthController {
private AuthenticationProvider authProvider;
public AuthController(AuthenticationProvider provider) {
this.authProvider = provider;
}
@PostMapping
public ResponseEntity<String> login(@RequestParam String credential) {
if (authProvider.authenticate(credential)) {
return ResponseEntity.ok(“Authentication successful!”);
} else {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(“Authentication failed.”);
}
}
}
How this relates to your work at Intuit:
- Spring Security separates authentication logic using AuthenticationProvider classes, not in controllers.
- This improves security and scalability for OAuth, JWT, and database authentication.
- How does Spring Security use abstraction to handle different types of authentication mechanisms?
Spring Security uses abstraction by providing common interfaces that different authentication mechanisms implement.
- AuthenticationProvider – Defines how authentication is performed.
- UserDetailsService – Retrieves user details from a database.
- FilterChainProxy – Manages authentication filters (e.g., OAuth, JWT).
Example of an AuthenticationProvider Implementation in Spring Security:
@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String username = authentication.getName();
String password = authentication.getCredentials().toString();
if (“admin”.equals(username) && “password”.equals(password)) {
return new UsernamePasswordAuthenticationToken(username, password, List.of(new SimpleGrantedAuthority(“ROLE_ADMIN”)));
} else {
throw new BadCredentialsException(“Invalid credentials”);
}
}
@Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
How this relates to your work at Hexaware:
- You worked with Spring Security filters & authentication mechanisms.
- Spring abstracts authentication logic using AuthenticationProvider, UserDetailsService, and other components.
- Can you create an abstract class for UserValidation that enforces validation rules for all user types?
We can create an abstract class that enforces validation rules, which child classes must implement.
Implementation:
// Abstract class for user validation
abstract class UserValidation {
protected String username;
protected String email;
public UserValidation(String username, String email) {
this.username = username;
this.email = email;
}
public abstract boolean validate();
protected boolean isValidEmail() {
return email.contains(“@”) && email.endsWith(“.com”);
}
}
// Admin validation with additional rules
class AdminValidation extends UserValidation {
public AdminValidation(String username, String email) {
super(username, email);
}
@Override
public boolean validate() {
return username.length() >= 5 && isValidEmail();
}
}
// Usage Example
public class ValidationDemo {
public static void main(String[] args) {
UserValidation adminValidation = new AdminValidation(“adminUser”, “admin@example.com”);
System.out.println(adminValidation.validate()); // true
}
}
How this relates to your work at Intuit:
- You implemented user validation in Spring Security using @Valid and custom validators.
Implementation:
// Abstract class defining authentication method
abstract class AuthenticationProvider {
public abstract boolean validateCredentials(String credential);
}
// Database Authentication Provider
class DatabaseAuthProvider extends AuthenticationProvider {
@Override
public boolean validateCredentials(String credential) {
System.out.println(“Validating credentials via Database…”);
// Simulate database lookup (In real-world, query DB)
return “user:password”.equals(credential);
}
}
// OAuth Authentication Provider
class OAuthAuthProvider extends AuthenticationProvider {
@Override
public boolean validateCredentials(String credential) {
System.out.println(“Validating credentials via OAuth…”);
// Simulate OAuth token validation
return “validOAuthToken”.equals(credential);
}
}
// LoginService that dynamically selects authentication strategy
class LoginService {
private AuthenticationProvider authProvider;
public LoginService(AuthenticationProvider authProvider) {
this.authProvider = authProvider;
}
public boolean login(String credential) {
return authProvider.validateCredentials(credential);
}
}
// Usage Example
public class AuthDemo {
public static void main(String[] args) {
// Using Database Authentication
LoginService dbLoginService = new LoginService(new DatabaseAuthProvider());
System.out.println(“Database Authentication Result: ” + dbLoginService.login(“user:password”)); // true
// Using OAuth Authentication
LoginService oauthLoginService = new LoginService(new OAuthAuthProvider());
System.out.println(“OAuth Authentication Result: ” + oauthLoginService.login(“validOAuthToken”)); // true
}
}
Key Features in this Implementation
- Abstract Class (AuthenticationProvider)
- Defines a common method validateCredentials().
- Ensures all authentication strategies follow the same interface.
- Polymorphism via Subclasses (DatabaseAuthProvider, OAuthAuthProvider)
- Each class provides its own implementation of validateCredentials().
- New authentication strategies can be added easily.
- LoginService Dynamically Selects Authentication Strategy
- Uses dependency injection to receive AuthenticationProvider.
- Allows switching authentication strategies without modifying existing code.
How This Relates to Your Work at Intuit
- You implemented OAuth & database authentication in Spring Security.
- This structure is similar to Spring Security’s AuthenticationProvider, where different authentication mechanisms can be plugged in dynamically.
- Helps with scalability and clean code architecture.