Wednesday, April 24, 2024
HomeJavaMake your safety coverage auditable

Make your safety coverage auditable


I’ll maintain utilizing Spring Boot within the following as a result of I’m acquainted with it. The Spring Boot software presents a REST endpoint to test workers’ salaries.

The precise use case is taken from the Open Coverage Agent web site (extra later):

Create a coverage that permits customers to request their very own wage in addition to the wage of their direct subordinates.

  1. Authenticate an HTTP request as coming from a recognized consumer
  2. Verify whether or not the consumer has entry to the wage information

In some other case, return a 401.

I’ll go an authentication token within the request to maintain issues easy. I gained’t depend on a devoted authentication/authorization backend, similar to Keycloak, however it needs to be the same method if you happen to do.

To allow Spring Safety on the app, we have to add the Spring Boot Safety Starter.

pom.xml

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

We additionally must allow Spring Safety to work its magic:

@SpringBootApplication
@EnableWebSecurity
class SecureBootApplication

With these two steps in place, we are able to begin securing the applying in line with the above requirement:

inside enjoyable safety() = beans {                                       (1)
    bean {
        val http = ref<HttpSecurity>()
        http {
            authorizeRequests {
                authorize("/finance/wage/**", authenticated)           (2)
            }
            addFilterBefore<UsernamePasswordAuthenticationFilter>(
                TokenAuthenticationFilter(ref())                        (3)
            )
            httpBasic { disable() }
            csrf { disable() }
            logout { disable() }
            sessionManagement {
                sessionCreationPolicy = SessionCreationPolicy.STATELESS
            }
        }
        http.construct()
    }
    bean { TokenAuthenticationManager(ref(), ref()) }                   (4)
}

1 Use the Kotlin Beans DSL – as a result of I can
2 Solely enable entry to the endpoint to authenticated customers
3 Add a filter within the filter chain to switch common authentication
4 Add a customized authentication supervisor

Requests appear to be the next:

curl -H 'Authorization: xyz'  localhost:9080/finance/wage/bob

The filter extracts from the request the required information used to resolve whether or not to permit the request or not:

TokenAuthenticationFilter.kt

inside class TokenAuthenticationFilter(authManager: AuthenticationManager) :
    AbstractAuthenticationProcessingFilter("/finance/wage/**", authManager) {

    override enjoyable attemptAuthentication(req: HttpServletRequest, resp: HttpServletResponse): Authentication {
        val header = req.getHeader("Authorization")                   (1)
        val path = req.servletPath.cut up("https://weblog.frankel.ch/")                         (2)
        val token = KeyToken(header, path)                            (3)
        return authenticationManager.authenticate(token)              (4)
    }

    // override enjoyable successfulAuthentication(
}

1 Get the authentication token
2 Get the trail
3 Wrap it beneath a devoted construction
4 Attempt to authenticate the token

In flip, the supervisor tries to authenticate the token:

TokenAuthenticationManager.kt

inside class TokenAuthenticationManager(
    non-public val accountRepo: AccountRepository,
    non-public val employeeRepo: EmployeeRepository
) : AuthenticationManager {
  override enjoyable authenticate(authentication: Authentication): Authentication {
    val token = authentication.credentials as String? ?:                       (1)
        throw BadCredentialsException("No token handed")
    val account = accountRepo.findByPassword(token).orElse(null) ?:             (2)
        throw BadCredentialsException("Invalid token")
    val path = authentication.particulars as Record<String>
    val accountId = account.id
    val section = path.final()
    if (section == accountId) return authentication.withPrincipal(accountId)   (3)
    val worker = employeeRepo.findById(section).orElse(null)                  (4)
    val managerUserName = worker?.supervisor?.userName
    if (managerUserName != null && managerUserName == accountId)               (5)
        return authentication.withPrincipal(accountId)                         (5)
    throw InsufficientAuthenticationException("Incorrect token")                (6)
  }
}

1 Get the authorization token handed from the filter
2 Attempt to discover the account that has this token. For simplicity’s sake, the token is saved in plain textual content with out hashing
3 If the account tries to entry its information, enable it
4 If not, we should load the hierarchy from one other repo.
5 If the account makes an attempt to entry information from an worker they handle, enable it.
6 Else, deny it.

The entire movement could be summarized as the next:

Spring Security Obfuscated Flow

Now, we are able to attempt some requests.

curl -H 'Authorization: bob' localhost:9080/finance/wage/bob

bob asks for his personal wage, and it really works.

curl -H 'Authorization: bob' localhost:9080/finance/wage/alice

bob asks for the wage of considered one of his subordinates, and it really works as properly.

curl -H 'Authorization: bob' localhost:9080/finance/wage/alice

alice asks for her supervisor’s wage, which isn’t allowed.

The code above works completely however has one massive challenge: there’s no solution to audit the logic. One should know Kotlin and the way Spring Safety works to make sure the implementation is sound.

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments