-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Update datadog example app to include traces and add to cart
- Loading branch information
Showing
15 changed files
with
743 additions
and
201 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
167 changes: 152 additions & 15 deletions
167
...adog/src/main/java/com/commercetools/sdk/examples/springmvc/config/CtpSecurityConfig.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,32 +1,169 @@ | ||
|
||
package com.commercetools.sdk.examples.springmvc.config; | ||
|
||
import java.time.Duration; | ||
|
||
import com.commercetools.api.client.ProjectApiRoot; | ||
import com.commercetools.api.defaultconfig.ApiRootBuilder; | ||
import com.commercetools.api.defaultconfig.ServiceRegion; | ||
|
||
import com.commercetools.sdk.examples.springmvc.service.CtpReactiveAuthenticationManager; | ||
import com.commercetools.sdk.examples.springmvc.service.MeRepository; | ||
import io.vrap.rmf.base.client.ApiHttpClient; | ||
import io.vrap.rmf.base.client.oauth2.ClientCredentials; | ||
import io.vrap.rmf.base.client.oauth2.TokenStorage; | ||
|
||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; | ||
import org.springframework.security.config.annotation.web.builders.HttpSecurity; | ||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; | ||
import org.springframework.http.HttpMethod; | ||
import org.springframework.security.authentication.ReactiveAuthenticationManager; | ||
import org.springframework.security.authentication.ReactiveAuthenticationManagerResolver; | ||
import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity; | ||
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity; | ||
import org.springframework.security.config.web.server.SecurityWebFiltersOrder; | ||
import org.springframework.security.config.web.server.ServerHttpSecurity; | ||
import org.springframework.security.web.SecurityFilterChain; | ||
import org.springframework.security.core.Authentication; | ||
import org.springframework.security.web.server.SecurityWebFilterChain; | ||
import org.springframework.security.web.server.WebFilterExchange; | ||
import org.springframework.security.web.server.authentication.*; | ||
import org.springframework.security.web.server.authentication.logout.*; | ||
import org.springframework.security.web.server.context.ServerSecurityContextRepository; | ||
import org.springframework.security.web.server.context.WebSessionServerSecurityContextRepository; | ||
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers; | ||
import org.springframework.stereotype.Component; | ||
import org.springframework.web.server.ServerWebExchange; | ||
import org.springframework.web.server.WebSession; | ||
import reactor.core.publisher.Mono; | ||
|
||
@Configuration | ||
@EnableWebSecurity | ||
@EnableMethodSecurity | ||
@EnableWebFluxSecurity | ||
@EnableReactiveMethodSecurity | ||
public class CtpSecurityConfig { | ||
private final ReactiveAuthenticationManagerResolver<ServerWebExchange> authenticationManagerResolver; | ||
|
||
@Autowired | ||
public CtpSecurityConfig( | ||
final ReactiveAuthenticationManagerResolver<ServerWebExchange> authenticationManagerResolver) { | ||
this.authenticationManagerResolver = authenticationManagerResolver; | ||
} | ||
|
||
@Bean | ||
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { | ||
http | ||
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) throws Exception { | ||
ServerSecurityContextRepository securityContextRepository = new WebSessionServerSecurityContextRepository(); | ||
return http.securityContextRepository(securityContextRepository) | ||
.anonymous() | ||
.and() | ||
.authorizeHttpRequests((requests) -> requests | ||
.requestMatchers("**").permitAll() | ||
.requestMatchers("/resources/**").permitAll() | ||
.anyRequest().permitAll() | ||
); | ||
.addFilterBefore(new LoginWebFilter(authenticationManagerResolver, securityContextRepository), | ||
SecurityWebFiltersOrder.FORM_LOGIN) | ||
.logout() | ||
.logoutUrl("/logout") | ||
.requiresLogout(ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, "/logout")) | ||
.logoutHandler(new DelegatingServerLogoutHandler(new WebSessionServerLogoutHandler(), | ||
new SecurityContextServerLogoutHandler())) | ||
.logoutSuccessHandler(new RedirectServerLogoutSuccessHandler()) | ||
.and() | ||
.formLogin() | ||
.loginPage("/login") | ||
.requiresAuthenticationMatcher(ServerWebExchangeMatchers.pathMatchers("none")) | ||
.authenticationManager(Mono::just) | ||
.and() | ||
.authorizeExchange() | ||
.pathMatchers("/login") | ||
.permitAll() | ||
.pathMatchers("/") | ||
.permitAll() | ||
.pathMatchers("/resources/**") | ||
.permitAll() | ||
.pathMatchers("/home") | ||
.permitAll() | ||
.pathMatchers("/p/**") | ||
.permitAll() | ||
.pathMatchers("/cart/**") | ||
.permitAll() | ||
.pathMatchers("/me/**") | ||
.authenticated() | ||
.anyExchange() | ||
.authenticated() | ||
.and() | ||
.build(); | ||
} | ||
|
||
@Component | ||
public static class CtpReactiveAuthenticationManagerResolver | ||
implements ReactiveAuthenticationManagerResolver<ServerWebExchange> { | ||
private final ApiHttpClient apiHttpClient; | ||
|
||
@Value(value = "${ctp.client.id}") | ||
private String clientId; | ||
|
||
@Value(value = "${ctp.client.secret}") | ||
private String clientSecret; | ||
|
||
@Value(value = "${ctp.project.key}") | ||
private String projectKey; | ||
|
||
private ClientCredentials credentials() { | ||
return ClientCredentials.of().withClientId(clientId).withClientSecret(clientSecret).build(); | ||
} | ||
|
||
@Autowired | ||
public CtpReactiveAuthenticationManagerResolver(final ApiHttpClient apiHttpClient) { | ||
this.apiHttpClient = apiHttpClient; | ||
} | ||
|
||
@Override | ||
public Mono<ReactiveAuthenticationManager> resolve(final ServerWebExchange context) { | ||
return Mono.just(new CtpReactiveAuthenticationManager(meClient(apiHttpClient, context.getSession()), | ||
credentials(), projectKey)); | ||
} | ||
|
||
private ProjectApiRoot meClient(final ApiHttpClient client, final Mono<WebSession> session) { | ||
TokenStorage storage = new SessionTokenStorage(session); | ||
|
||
ApiRootBuilder builder = ApiRootBuilder.of(client) | ||
.withApiBaseUrl(ServiceRegion.GCP_EUROPE_WEST1.getApiUrl()) | ||
.withProjectKey(projectKey) | ||
.withAnonymousRefreshFlow(credentials(), ServiceRegion.GCP_EUROPE_WEST1, storage); | ||
|
||
return builder.build(projectKey); | ||
} | ||
} | ||
|
||
public static class LoginWebFilter extends AuthenticationWebFilter { | ||
public LoginWebFilter(ReactiveAuthenticationManagerResolver<ServerWebExchange> authenticationManager, | ||
ServerSecurityContextRepository securityContextRepository) { | ||
super(authenticationManager); | ||
setServerAuthenticationConverter(new ServerFormLoginAuthenticationConverter()); | ||
setRequiresAuthenticationMatcher(ServerWebExchangeMatchers.pathMatchers(HttpMethod.POST, "/login")); | ||
setAuthenticationFailureHandler(new RedirectServerAuthenticationFailureHandler("/login?error")); | ||
setAuthenticationSuccessHandler(new WebFilterChainServerAuthenticationSuccessHandler()); | ||
setSecurityContextRepository(securityContextRepository); | ||
} | ||
} | ||
|
||
private static class WebFilterChainServerAuthenticationSuccessHandler | ||
implements ServerAuthenticationSuccessHandler { | ||
|
||
@Override | ||
public Mono<Void> onAuthenticationSuccess(WebFilterExchange webFilterExchange, Authentication authentication) { | ||
ServerWebExchange exchange = webFilterExchange.getExchange(); | ||
TokenStorage storage = new SessionTokenStorage(exchange.getSession()); | ||
TokenGrantedAuthority authority = (TokenGrantedAuthority) authentication.getAuthorities() | ||
.stream() | ||
.filter(grantedAuthority -> grantedAuthority instanceof TokenGrantedAuthority) | ||
.findFirst() | ||
.get(); | ||
storage.setToken(authority.getToken()); | ||
|
||
return http.build(); | ||
if (authentication.getPrincipal() instanceof CtpUserDetails) { | ||
exchange.getSession().blockOptional(Duration.ofMillis(500)).ifPresent(session -> { | ||
session.getAttributes() | ||
.put(MeRepository.SESSION_CART, ((CtpUserDetails) authentication.getPrincipal()).getCart()); | ||
session.save(); | ||
}); | ||
} | ||
return webFilterExchange.getChain().filter(exchange); | ||
} | ||
} | ||
} |
70 changes: 70 additions & 0 deletions
70
...datadog/src/main/java/com/commercetools/sdk/examples/springmvc/config/CtpUserDetails.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
package com.commercetools.sdk.examples.springmvc.config; | ||
|
||
import java.util.*; | ||
import java.util.stream.Collectors; | ||
import java.util.stream.Stream; | ||
|
||
import com.commercetools.api.models.cart.CartReference; | ||
import com.commercetools.api.models.customer.Customer; | ||
|
||
import org.springframework.security.core.GrantedAuthority; | ||
import org.springframework.security.core.userdetails.UserDetails; | ||
import reactor.util.annotation.Nullable; | ||
|
||
public class CtpUserDetails implements UserDetails { | ||
private final String customerName; | ||
private final CartReference cartReference; | ||
private final Collection<? extends GrantedAuthority> authorities; | ||
|
||
public CtpUserDetails(Customer customer, CartReference cart, Collection<? extends GrantedAuthority> authorities) { | ||
this.customerName = userName(customer); | ||
this.cartReference = cart; | ||
this.authorities = authorities; | ||
} | ||
|
||
private String userName(Customer customer) { | ||
return Stream.of(customer.getFirstName(), customer.getMiddleName(), customer.getLastName()) | ||
.filter(s -> s != null && !s.isEmpty()) | ||
.collect(Collectors.joining(" ")); | ||
} | ||
|
||
@Nullable | ||
public CartReference getCart() { | ||
return cartReference; | ||
} | ||
|
||
@Override | ||
public Collection<? extends GrantedAuthority> getAuthorities() { | ||
return authorities; | ||
} | ||
|
||
@Override | ||
public String getPassword() { | ||
return null; | ||
} | ||
|
||
@Override | ||
public String getUsername() { | ||
return customerName; | ||
} | ||
|
||
@Override | ||
public boolean isAccountNonExpired() { | ||
return true; | ||
} | ||
|
||
@Override | ||
public boolean isAccountNonLocked() { | ||
return true; | ||
} | ||
|
||
@Override | ||
public boolean isCredentialsNonExpired() { | ||
return true; | ||
} | ||
|
||
@Override | ||
public boolean isEnabled() { | ||
return true; | ||
} | ||
} |
73 changes: 73 additions & 0 deletions
73
...datadog/src/main/java/com/commercetools/sdk/examples/springmvc/config/MeClientFilter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
package com.commercetools.sdk.examples.springmvc.config; | ||
|
||
import com.commercetools.api.client.ProjectApiRoot; | ||
import com.commercetools.api.defaultconfig.ApiRootBuilder; | ||
import com.commercetools.api.defaultconfig.ServiceRegion; | ||
|
||
import io.vrap.rmf.base.client.*; | ||
import io.vrap.rmf.base.client.oauth2.ClientCredentials; | ||
import io.vrap.rmf.base.client.oauth2.TokenStorage; | ||
|
||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.stereotype.Component; | ||
import org.springframework.web.server.ServerWebExchange; | ||
import org.springframework.web.server.WebFilter; | ||
import org.springframework.web.server.WebFilterChain; | ||
import org.springframework.web.server.WebSession; | ||
import reactor.core.publisher.Mono; | ||
|
||
import java.util.Map; | ||
|
||
@Component | ||
public class MeClientFilter implements WebFilter { | ||
private final ApiHttpClient client; | ||
|
||
@Value(value = "${ctp.client.id}") | ||
private String clientId; | ||
|
||
@Value(value = "${ctp.client.secret}") | ||
private String clientSecret; | ||
|
||
@Value(value = "${ctp.project.key}") | ||
private String projectKey; | ||
|
||
private ClientCredentials credentials() { | ||
return ClientCredentials.of().withClientId(clientId).withClientSecret(clientSecret).build(); | ||
} | ||
|
||
@Autowired | ||
public MeClientFilter(ApiHttpClient client) { | ||
this.client = client; | ||
} | ||
|
||
@Override | ||
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) { | ||
final ContextApiHttpClient contextClient = contextClient(client, new MDCContext(Map.of("requestId", exchange.getRequest().getId()))); | ||
final ProjectApiRoot meClient = exchange.getAttributeOrDefault("meClient", | ||
meClient(contextClient, exchange.getSession())); | ||
exchange.getAttributes().put("meClient", meClient); | ||
exchange.getAttributes().put("contextRoot", contextRoot(contextClient)); | ||
|
||
return chain.filter(exchange); | ||
} | ||
|
||
private ProjectApiRoot meClient(ApiHttpClient client, Mono<WebSession> session) { | ||
TokenStorage storage = new SessionTokenStorage(session); | ||
|
||
ApiRootBuilder builder = ApiRootBuilder.of(client) | ||
.withApiBaseUrl(ServiceRegion.GCP_EUROPE_WEST1.getApiUrl()) | ||
.withProjectKey(projectKey) | ||
.withAnonymousRefreshFlow(credentials(), ServiceRegion.GCP_EUROPE_WEST1, storage); | ||
|
||
return builder.build(projectKey); | ||
} | ||
|
||
private ProjectApiRoot contextRoot(ContextApiHttpClient apiHttpClient) { | ||
return ProjectApiRoot.fromClient(projectKey, apiHttpClient); | ||
} | ||
|
||
private ContextApiHttpClient contextClient(ApiHttpClient client, Context context) { | ||
return ContextApiHttpClient.of(client, context); | ||
} | ||
} |
Oops, something went wrong.