[BAEL-5584] Article code (#12157)
This commit is contained in:
+13
@@ -0,0 +1,13 @@
|
||||
package com.baeldung.security.opa;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class Application {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(Application.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package com.baeldung.security.opa.config;
|
||||
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
@Configuration
|
||||
@RequiredArgsConstructor
|
||||
@EnableConfigurationProperties(OpaProperties.class)
|
||||
public class OpaConfiguration {
|
||||
|
||||
private final OpaProperties opaProperties;
|
||||
|
||||
@Bean
|
||||
public WebClient opaWebClient(WebClient.Builder builder) {
|
||||
|
||||
return builder
|
||||
.baseUrl(opaProperties.getEndpoint())
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package com.baeldung.security.opa.config;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@ConfigurationProperties(prefix = "opa")
|
||||
@Data
|
||||
public class OpaProperties {
|
||||
@Nonnull
|
||||
private String endpoint = "http://localhost:8181";
|
||||
}
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
package com.baeldung.security.opa.config;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.client.reactive.ClientHttpRequest;
|
||||
import org.springframework.security.authentication.AnonymousAuthenticationToken;
|
||||
import org.springframework.security.authorization.AuthorizationDecision;
|
||||
import org.springframework.security.authorization.ReactiveAuthorizationManager;
|
||||
import org.springframework.security.config.web.server.ServerHttpSecurity;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.web.server.SecurityWebFilterChain;
|
||||
import org.springframework.security.web.server.authorization.AuthorizationContext;
|
||||
import org.springframework.web.reactive.function.BodyInserter;
|
||||
import org.springframework.web.reactive.function.BodyInserters;
|
||||
import org.springframework.web.reactive.function.client.ClientResponse;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
@Configuration
|
||||
public class SecurityConfiguration {
|
||||
|
||||
|
||||
@Bean
|
||||
public SecurityWebFilterChain accountAuthorization(ServerHttpSecurity http, @Qualifier("opaWebClient")WebClient opaWebClient) {
|
||||
|
||||
// @formatter:on
|
||||
return http
|
||||
.httpBasic()
|
||||
.and()
|
||||
.authorizeExchange(exchanges -> {
|
||||
exchanges
|
||||
.pathMatchers("/account/*")
|
||||
.access(opaAuthManager(opaWebClient));
|
||||
})
|
||||
.build();
|
||||
// @formatter:on
|
||||
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ReactiveAuthorizationManager<AuthorizationContext> opaAuthManager(WebClient opaWebClient) {
|
||||
|
||||
return (auth, context) -> {
|
||||
return opaWebClient.post()
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.body(toAuthorizationPayload(auth,context), Map.class)
|
||||
.exchangeToMono(this::toDecision);
|
||||
};
|
||||
}
|
||||
|
||||
private Mono<AuthorizationDecision> toDecision(ClientResponse response) {
|
||||
|
||||
if ( !response.statusCode().is2xxSuccessful()) {
|
||||
return Mono.just(new AuthorizationDecision(false));
|
||||
}
|
||||
|
||||
return response
|
||||
.bodyToMono(ObjectNode.class)
|
||||
.map(node -> {
|
||||
boolean authorized = node.path("result").path("authorized").asBoolean(false);
|
||||
return new AuthorizationDecision(authorized);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private Publisher<Map<String,Object>> toAuthorizationPayload(Mono<Authentication> auth, AuthorizationContext context) {
|
||||
// @formatter:off
|
||||
return auth
|
||||
.defaultIfEmpty(new AnonymousAuthenticationToken("**ANONYMOUS**", new Object(), Arrays.asList(new SimpleGrantedAuthority("ANONYMOUS"))))
|
||||
.map( a -> {
|
||||
|
||||
Map<String,String> headers = context.getExchange().getRequest()
|
||||
.getHeaders()
|
||||
.toSingleValueMap();
|
||||
|
||||
Map<String,Object> attributes = ImmutableMap.<String,Object>builder()
|
||||
.put("principal",a.getName())
|
||||
.put("authorities",
|
||||
a.getAuthorities()
|
||||
.stream()
|
||||
.map(g -> g.getAuthority())
|
||||
.collect(Collectors.toList()))
|
||||
.put("uri", context.getExchange().getRequest().getURI().getPath())
|
||||
.put("headers",headers)
|
||||
.build();
|
||||
|
||||
Map<String,Object> input = ImmutableMap.<String,Object>builder()
|
||||
.put("input",attributes)
|
||||
.build();
|
||||
|
||||
return input;
|
||||
});
|
||||
// @formatter:on
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package com.baeldung.security.opa.controller;
|
||||
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.baeldung.security.opa.domain.Account;
|
||||
import com.baeldung.security.opa.service.AccountService;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
public class AccountController {
|
||||
|
||||
private final AccountService accountService;
|
||||
|
||||
@GetMapping("/account/{accountId}")
|
||||
public Mono<Account> getAccount(@PathVariable("accountId") String accountId) {
|
||||
return accountService.findByAccountId(accountId);
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package com.baeldung.security.opa.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class Account {
|
||||
|
||||
private String id;
|
||||
private BigDecimal balance;
|
||||
private String currency;
|
||||
|
||||
|
||||
public static Account of(String id, BigDecimal balance, String currency) {
|
||||
Account acc = new Account();
|
||||
acc.setId(id);
|
||||
acc.setBalance(balance);
|
||||
acc.setCurrency(currency);
|
||||
|
||||
return acc;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package com.baeldung.security.opa.service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.baeldung.security.opa.domain.Account;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* @author Philippe
|
||||
*
|
||||
*/
|
||||
@Service
|
||||
public class AccountService {
|
||||
|
||||
private Map<String, Account> accounts = ImmutableMap.<String, Account>builder()
|
||||
.put("0001", Account.of("0001", BigDecimal.valueOf(100.00), "USD"))
|
||||
.put("0002", Account.of("0002", BigDecimal.valueOf(101.00), "EUR"))
|
||||
.put("0003", Account.of("0003", BigDecimal.valueOf(102.00), "BRL"))
|
||||
.put("0004", Account.of("0004", BigDecimal.valueOf(103.00), "AUD"))
|
||||
.put("0005", Account.of("0005", BigDecimal.valueOf(10400.00), "JPY"))
|
||||
.build();
|
||||
|
||||
|
||||
public Mono<Account> findByAccountId(String accountId) {
|
||||
return Mono.just(accounts.get(accountId))
|
||||
.switchIfEmpty(Mono.error(new IllegalArgumentException("invalid.account")));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
# OPA configuration properties
|
||||
opa:
|
||||
endpoint: http://localhost:8181/v1/data/baeldung/auth/account
|
||||
Reference in New Issue
Block a user