Commit 880da07a authored by Brian Long's avatar Brian Long
Browse files

added /app/rest support for non-UI requests

parent faba551a
Loading
Loading
Loading
Loading
+53 −5
Original line number Diff line number Diff line
package com.inteligr8.activiti.auth.oauth;

import static org.springframework.security.config.Customizer.withDefaults;
import static org.springframework.security.web.util.matcher.AntPathRequestMatcher.antMatcher;

import org.apache.commons.lang3.StringUtils;
@@ -14,11 +15,17 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.util.matcher.AndRequestMatcher;
import org.springframework.security.web.util.matcher.RequestHeaderRequestMatcher;

import com.activiti.domain.idm.Capabilities;
import com.activiti.security.ActivitiAppRequestHeaderService;
import com.activiti.security.ActivitiRestAuthorizationService;
import com.activiti.security.ProtectedPaths;
import com.activiti.security.identity.service.config.IdentityServiceEnabledCondition;
import com.inteligr8.activiti.auth.service.JwtAuthenticationProvider;
@@ -43,6 +50,12 @@ public class IdentityServiceConfigurationOverride {
    @Autowired
    private JwtAuthenticationProvider jwtAuthenticationProvider;

    @Autowired
    private ActivitiAppRequestHeaderService appRequestHeaderService;

    @Autowired
    private ActivitiRestAuthorizationService restAuthorizationService;

    @Bean("inteligr8.clientRegistrationRepository")
    @Primary
    public ClientRegistrationRepository clientRegistrationRepository() {
@@ -73,11 +86,13 @@ public class IdentityServiceConfigurationOverride {
    }

    /**
     * Slightly lower priority than the one provided OOTB.  This
     * allows for the bean injection of the JwtAuthenticationConverter.
     * Slightly higher priority than the one provided OOTB.  This allows for
     * the bean injection of the `JwtAuthenticationConverter`.
     * 
     * A lower priority means it is applied last.  This means it replaces the
     * JwtAuthenticationConverter provided by Alfresco OOTB.
     * This is basically a copy of what is provided OOTB, but:
     * 
     * - The ability to configure the `JwtAuthenticationConverter`.
     * - Allow non-UI access to `/app/rest/*`
     * 
     * @see com.activiti.security.identity.service.config.IdentityServiceConfigurationApi#identityServiceApiWebSecurity
     */
@@ -85,11 +100,44 @@ public class IdentityServiceConfigurationOverride {
    @Order(-5)
    public SecurityFilterChain identityServiceApiWebSecurity(HttpSecurity http) throws Exception {
        http
            .securityMatcher(antMatcher(ProtectedPaths.API_URL_PATH + "/**"))
            .securityMatchers(matchers -> {
                matchers.requestMatchers(
                        // same as OOTB
                        antMatcher(ProtectedPaths.API_URL_PATH + "/**"),

                        // want to also allow non-UI access to the the protected API
                        // we do this for anything with an `Authorization` header, as the UI uses session-based authorization
                        new AndRequestMatcher(new RequestHeaderRequestMatcher("Authorization"), antMatcher(ProtectedPaths.APP_URL_PATH + "/rest/**"))
                );
            })
            .csrf(csrf -> {
                csrf.disable();
            })
            .cors(withDefaults())
            .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) // Stores no Session for API calls
            .oauth2ResourceServer(oauth2 ->
                oauth2.jwt(jwtConfigurer -> {
                    // here is where we are injecting a Spring extendible `JwtAuthenticationConverter`.
                    jwtConfigurer.jwtAuthenticationConverter(this.jwtAuthenticationProvider.create());
                })
            )
            .authorizeHttpRequests(request ->
                request
                    // same as OOTB
                    .requestMatchers(antMatcher(ProtectedPaths.API_URL_PATH + "/enterprise/**"))
                    .access(this.appRequestHeaderService)
                    .requestMatchers(antMatcher(ProtectedPaths.API_URL_PATH + "/**"))
                    .access(this.restAuthorizationService)
                    
                    // borrowed from OOTB /app/rest security
                    .requestMatchers(antMatcher(ProtectedPaths.APP_URL_PATH + "/rest/reporting/**"))
                    .hasAuthority(Capabilities.ACCESS_REPORTS)
                    
                    .requestMatchers(
                            antMatcher(ProtectedPaths.API_URL_PATH + "/**"),
                            antMatcher(ProtectedPaths.APP_URL_PATH + "/rest/**")
                    )
                    .authenticated()
            );

        return http.build();
+67 −6
Original line number Diff line number Diff line
@@ -101,14 +101,14 @@
        "profile",
        "roles",
        "basic",
        "email"
        "email",
        "microprofile-jwt"
      ],
      "optionalClientScopes": [
        "address",
        "phone",
        "organization",
        "offline_access",
        "microprofile-jwt"
        "offline_access"
      ]
    },
    {
@@ -156,15 +156,76 @@
        "profile",
        "roles",
        "basic",
        "email"
        "email",
        "microprofile-jwt"
      ],
      "optionalClientScopes": [
        "address",
        "phone",
        "organization",
        "offline_access",
        "microprofile-jwt"
        "offline_access"
      ]
    },
    {
      "clientId": "cli",
      "name": "Command Line Tools",
      "description": "",
      "rootUrl": "",
      "adminUrl": "",
      "baseUrl": "",
      "surrogateAuthRequired": false,
      "enabled": true,
      "alwaysDisplayInConsole": false,
      "clientAuthenticatorType": "client-secret",
      "secret": "eJa5W7bv4ohFbr7QRtaCk0eccRFoYM5x",
      "redirectUris": [
        "/*"
      ],
      "webOrigins": [
        "/*"
      ],
      "notBefore": 0,
      "bearerOnly": false,
      "consentRequired": false,
      "standardFlowEnabled": false,
      "implicitFlowEnabled": false,
      "directAccessGrantsEnabled": false,
      "serviceAccountsEnabled": true,
      "publicClient": false,
      "frontchannelLogout": true,
      "protocol": "openid-connect",
      "attributes": {
        "realm_client": "false",
        "oidc.ciba.grant.enabled": "false",
        "client.secret.creation.time": "1747506410",
        "backchannel.logout.session.required": "true",
        "standard.token.exchange.enabled": "true",
        "oauth2.device.authorization.grant.enabled": "false",
        "backchannel.logout.revoke.offline.tokens": "false"
      },
      "authenticationFlowBindingOverrides": {},
      "fullScopeAllowed": true,
      "nodeReRegistrationTimeout": -1,
      "defaultClientScopes": [
        "web-origins",
        "acr",
        "profile",
        "roles",
        "basic",
        "email",
        "microprofile-jwt"
      ],
      "optionalClientScopes": [
        "address",
        "phone",
        "organization",
        "offline_access"
      ],
      "access": {
        "view": true,
        "configure": true,
        "manage": true
      }
    }
  ],
  "users": [
+35 −0
Original line number Diff line number Diff line
@keycloakRealm = my-app
@keycloakBaseUrl = http://localhost:8081
@oauthUrl = {{keycloakBaseUrl}}/realms/{{keycloakRealm}}
@keycloakTokenUrl = {{oauthUrl}}/protocol/openid-connect/token
@oauthClientId = cli
@oauthClientSecret = eJa5W7bv4ohFbr7QRtaCk0eccRFoYM5x
@apsBaseUrl = http://localhost:8080/activiti-app

### Token
# @name token
curl -LX POST {{keycloakTokenUrl}} \
    -H 'Content-type: application/x-www-form-urlencoded' \
    -d "grant_type=client_credentials" \
    -d "client_id={{oauthClientId}}" \
    -d "client_secret={{oauthClientSecret}}"

@accessToken = {{token.response.body.access_token}}
@auth = Bearer {{accessToken}}

### APS Version
# @name version
GET {{apsBaseUrl}}/api/enterprise/app-version
Authorization: Bearer {{accessToken}}

### APS Tenants
# @name tenants
GET {{apsBaseUrl}}/api/enterprise/admin/tenants
Authorization: Bearer {{accessToken}}

@tenantId = {{tenants.response.body.0.id}}

### APS Templates
# @name templates
GET {{apsBaseUrl}}/app/rest/document-templates?tenantId={{tenantId}}&start=0&size=10&sort=sort_by_name_asc
Authorization: Bearer {{accessToken}}