content: /*
   * Copyright 2017-2025 Hyland Software, Inc. and its affiliates.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  package org.activiti.cloud.services.notifications.qraphql.ws.security;
  
  import java.util.function.Function;
  import org.activiti.cloud.security.authorization.CustomAuthorizationManager;
  import org.activiti.cloud.services.common.security.jwt.JwtAccessTokenValidator;
  import org.activiti.cloud.services.common.security.jwt.JwtAdapter;
  import org.activiti.cloud.services.common.security.jwt.JwtUserInfoUriAuthenticationConverter;
  import org.activiti.cloud.services.notifications.qraphql.ws.security.tokenverifier.GraphQLAccessTokenVerifier;
  import org.activiti.cloud.services.notifications.qraphql.ws.security.tokenverifier.jwt.JwtAccessTokenVerifier;
  import org.springframework.beans.factory.annotation.Value;
  import org.springframework.boot.autoconfigure.AutoConfiguration;
  import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
  import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
  import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
  import org.springframework.context.annotation.Bean;
  import org.springframework.context.annotation.Configuration;
  import org.springframework.context.annotation.PropertySource;
  import org.springframework.context.annotation.PropertySources;
  import org.springframework.graphql.server.WebSocketGraphQlInterceptor;
  import org.springframework.graphql.server.support.BearerTokenAuthenticationExtractor;
  import org.springframework.security.oauth2.jwt.Jwt;
  import org.springframework.security.oauth2.jwt.JwtDecoder;
  import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
  
  @AutoConfiguration
  @ConditionalOnProperty(
      name = "spring.activiti.cloud.services.notification.graphql.ws.security.enabled",
      matchIfMissing = true
  )
  public class WebSocketMessageBrokerSecurityAutoConfiguration {
  
      @Bean
      @ConditionalOnMissingBean
      @ConditionalOnExpression("'${activiti.cloud.services.oauth2.iam-name}'!='keycloak'")
      public GraphQLAccessTokenVerifier jwtTokenVerifier(
          JwtAccessTokenValidator jwtAccessTokenValidator,
          JwtUserInfoUriAuthenticationConverter jwtUserInfoUriAuthenticationConverter,
          JwtDecoder jwtDecoder
      ) {
          return new JwtAccessTokenVerifier(
              jwtAccessTokenValidator,
              jwtUserInfoUriAuthenticationConverter,
              jwtDecoder,
              jwt -> jwt.getClaimAsStringList("role")
          );
      }
  
      @Bean
      @ConditionalOnMissingBean
      @ConditionalOnProperty(value = "activiti.cloud.services.oauth2.iam-name", havingValue = "keycloak")
      public GraphQLAccessTokenVerifier keycloakTokenVerifier(
          JwtAccessTokenValidator jwtAccessTokenValidator,
          JwtUserInfoUriAuthenticationConverter jwtUserInfoUriAuthenticationConverter,
          JwtDecoder jwtDecoder,
          Function<Jwt, JwtAdapter> jwtAdapterSupplier
      ) {
          return new JwtAccessTokenVerifier(
              jwtAccessTokenValidator,
              jwtUserInfoUriAuthenticationConverter,
              jwtDecoder,
              jwt -> jwtAdapterSupplier.apply(jwt).getRoles()
          );
      }
  
      @Bean
      @ConditionalOnMissingBean
      public JWSAuthenticationManager keycloakWebSocketAuthManager(GraphQLAccessTokenVerifier keycloakTokenVerifier) {
          return new JWSAuthenticationManager(keycloakTokenVerifier);
      }
  
      @Configuration
      @PropertySources(
          value = {
              @PropertySource(value = "classpath:META-INF/graphql-security.properties"),
              @PropertySource(value = "classpath:graphql-security.properties", ignoreResourceNotFound = true),
          }
      )
      public static class SpringNativeWebSocketMessageBrokerSecurityConfiguration {
  
          @Value("${spring.activiti.cloud.services.notifications.graphql.ws.security.authorities:}")
          private String[] authorities;
  
          @Value("${spring.activiti.cloud.services.notifications.graphql.ws.security.permissions:}")
          private String[] permissions;
  
          @Bean
          JWSBearerTokenAuthenticationExtractor jwsBearerTokenAuthenticationExtractor() {
              return new JWSBearerTokenAuthenticationExtractor(new BearerTokenAuthenticationExtractor());
          }
  
          @Bean
          public WebSocketGraphQlInterceptor authenticationInterceptor(
              JWSAuthenticationManager jwsAuthenticationManager,
              JWSBearerTokenAuthenticationExtractor jwsBearerTokenAuthenticationExtractor
          ) {
              return new SecurityWebSocketInterceptor(
                  jwsBearerTokenAuthenticationExtractor,
                  jwsAuthenticationManager,
                  new CustomAuthorizationManager<RequestAuthorizationContext>(authorities, permissions)
              );
          }
      }
  }
  