Compare commits
6 Commits
Author | SHA1 | Date | |
---|---|---|---|
514ba6bae1 | |||
6c93637dbb | |||
65d80dc82d | |||
4859473938 | |||
99f5a7702c | |||
880da07a84 |
@@ -55,6 +55,7 @@ The following properties are used across the functionalities of this extension.
|
|||||||
| --------------------------------- | --------- | ----------- |
|
| --------------------------------- | --------- | ----------- |
|
||||||
| `auth-ext.externalId` | `oauth` | This will serve as the external ID for users and as the prefix for the external ID of groups created or searched by this extension. Anything without an external ID is considered internal. So mismatched external IDs are never considered for anything by this extension. |
|
| `auth-ext.externalId` | `oauth` | This will serve as the external ID for users and as the prefix for the external ID of groups created or searched by this extension. Anything without an external ID is considered internal. So mismatched external IDs are never considered for anything by this extension. |
|
||||||
| `auth-ext.tenant` | | A preselected tenant for all operations in this extension. Only required if there are multiple tenants. |
|
| `auth-ext.tenant` | | A preselected tenant for all operations in this extension. Only required if there are multiple tenants. |
|
||||||
|
| `auth-ext.sync.resyncInMillis` | `300000` | To prevent too many sync checks, how long between seeing the EXACT same token should we wait before doing another sync. The only time this matters is if the user is manually added to or removed from groups in APS by other means. Or those groups are deleted. |
|
||||||
|
|
||||||
### OAuth Authentication/Authorization
|
### OAuth Authentication/Authorization
|
||||||
|
|
||||||
|
2
pom.xml
2
pom.xml
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
<groupId>com.inteligr8.activiti</groupId>
|
<groupId>com.inteligr8.activiti</groupId>
|
||||||
<artifactId>auth-activiti-app-ext</artifactId>
|
<artifactId>auth-activiti-app-ext</artifactId>
|
||||||
<version>2.1.0</version>
|
<version>2.1.2</version>
|
||||||
|
|
||||||
<name>Authentication & Authorization for APS</name>
|
<name>Authentication & Authorization for APS</name>
|
||||||
<description>An Alfresco Process Service App extension providing improved authentication and authorization support.</description>
|
<description>An Alfresco Process Service App extension providing improved authentication and authorization support.</description>
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
package com.inteligr8.activiti.auth.oauth;
|
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 static org.springframework.security.web.util.matcher.AntPathRequestMatcher.antMatcher;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
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.context.annotation.Primary;
|
||||||
import org.springframework.core.annotation.Order;
|
import org.springframework.core.annotation.Order;
|
||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
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.ClientRegistration;
|
||||||
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
|
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
|
||||||
import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;
|
import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;
|
||||||
import org.springframework.security.web.SecurityFilterChain;
|
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.ProtectedPaths;
|
||||||
import com.activiti.security.identity.service.config.IdentityServiceEnabledCondition;
|
import com.activiti.security.identity.service.config.IdentityServiceEnabledCondition;
|
||||||
import com.inteligr8.activiti.auth.service.JwtAuthenticationProvider;
|
import com.inteligr8.activiti.auth.service.JwtAuthenticationProvider;
|
||||||
@@ -43,6 +50,12 @@ public class IdentityServiceConfigurationOverride {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private JwtAuthenticationProvider jwtAuthenticationProvider;
|
private JwtAuthenticationProvider jwtAuthenticationProvider;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ActivitiAppRequestHeaderService appRequestHeaderService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ActivitiRestAuthorizationService restAuthorizationService;
|
||||||
|
|
||||||
@Bean("inteligr8.clientRegistrationRepository")
|
@Bean("inteligr8.clientRegistrationRepository")
|
||||||
@Primary
|
@Primary
|
||||||
public ClientRegistrationRepository clientRegistrationRepository() {
|
public ClientRegistrationRepository clientRegistrationRepository() {
|
||||||
@@ -73,11 +86,13 @@ public class IdentityServiceConfigurationOverride {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Slightly lower priority than the one provided OOTB. This
|
* Slightly higher priority than the one provided OOTB. This allows for
|
||||||
* allows for the bean injection of the JwtAuthenticationConverter.
|
* the bean injection of the `JwtAuthenticationConverter`.
|
||||||
*
|
*
|
||||||
* A lower priority means it is applied last. This means it replaces the
|
* This is basically a copy of what is provided OOTB, but:
|
||||||
* JwtAuthenticationConverter provided by Alfresco OOTB.
|
*
|
||||||
|
* - The ability to configure the `JwtAuthenticationConverter`.
|
||||||
|
* - Allow non-UI access to `/app/rest/*`
|
||||||
*
|
*
|
||||||
* @see com.activiti.security.identity.service.config.IdentityServiceConfigurationApi#identityServiceApiWebSecurity
|
* @see com.activiti.security.identity.service.config.IdentityServiceConfigurationApi#identityServiceApiWebSecurity
|
||||||
*/
|
*/
|
||||||
@@ -85,11 +100,44 @@ public class IdentityServiceConfigurationOverride {
|
|||||||
@Order(-5)
|
@Order(-5)
|
||||||
public SecurityFilterChain identityServiceApiWebSecurity(HttpSecurity http) throws Exception {
|
public SecurityFilterChain identityServiceApiWebSecurity(HttpSecurity http) throws Exception {
|
||||||
http
|
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 ->
|
.oauth2ResourceServer(oauth2 ->
|
||||||
oauth2.jwt(jwtConfigurer -> {
|
oauth2.jwt(jwtConfigurer -> {
|
||||||
|
// here is where we are injecting a Spring extendible `JwtAuthenticationConverter`.
|
||||||
jwtConfigurer.jwtAuthenticationConverter(this.jwtAuthenticationProvider.create());
|
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();
|
return http.build();
|
||||||
|
@@ -4,10 +4,12 @@ import java.util.ArrayList;
|
|||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.core.convert.converter.Converter;
|
import org.springframework.core.convert.converter.Converter;
|
||||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
|
import org.springframework.security.oauth2.core.oidc.StandardClaimNames;
|
||||||
import org.springframework.security.oauth2.jwt.Jwt;
|
import org.springframework.security.oauth2.jwt.Jwt;
|
||||||
|
|
||||||
import com.activiti.security.identity.service.config.JwtAuthenticationToken;
|
import com.activiti.security.identity.service.config.JwtAuthenticationToken;
|
||||||
@@ -22,6 +24,9 @@ public class SyncingJwtAuthenticationConverter implements Converter<Jwt, Abstrac
|
|||||||
private final UserSyncService userSyncService;
|
private final UserSyncService userSyncService;
|
||||||
private final GroupSyncService groupSyncService;
|
private final GroupSyncService groupSyncService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private TokenRecaller tokenRecaller;
|
||||||
|
|
||||||
public SyncingJwtAuthenticationConverter(UserDetailsService userDetailsService, UserSyncService userSyncService, GroupSyncService groupSyncService) {
|
public SyncingJwtAuthenticationConverter(UserDetailsService userDetailsService, UserSyncService userSyncService, GroupSyncService groupSyncService) {
|
||||||
this.userDetailsService = userDetailsService;
|
this.userDetailsService = userDetailsService;
|
||||||
this.userSyncService = userSyncService;
|
this.userSyncService = userSyncService;
|
||||||
@@ -30,18 +35,24 @@ public class SyncingJwtAuthenticationConverter implements Converter<Jwt, Abstrac
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AbstractAuthenticationToken convert(Jwt source) {
|
public AbstractAuthenticationToken convert(Jwt source) {
|
||||||
this.logger.trace("convert({}, {})", source.getId(), source.getClaim("email"));
|
if (this.logger.isTraceEnabled()) {
|
||||||
|
this.logger.trace("convert({}, {})", source.getId(), source.getClaimAsString(StandardClaimNames.EMAIL));
|
||||||
try {
|
try {
|
||||||
this.logger.debug("jwt: {}", new ObjectMapper().registerModule(new JavaTimeModule()).writeValueAsString(source));
|
this.logger.trace("jwt: {}", new ObjectMapper().registerModule(new JavaTimeModule()).writeValueAsString(source));
|
||||||
} catch (JsonProcessingException jpe) {
|
} catch (JsonProcessingException jpe) {
|
||||||
this.logger.error("error", jpe);
|
this.logger.error("error", jpe);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.userSyncService.sync(source);
|
if (this.tokenRecaller.recall(source.getTokenValue())) {
|
||||||
this.groupSyncService.sync(source);
|
this.logger.trace("Skipping sync for '{}' as the same token was already recently sync'd", source.getClaimAsString(StandardClaimNames.EMAIL));
|
||||||
|
} else {
|
||||||
|
this.userSyncService.sync(source);
|
||||||
|
this.groupSyncService.sync(source);
|
||||||
|
this.tokenRecaller.add(source.getTokenValue());
|
||||||
|
}
|
||||||
|
|
||||||
UserDetails springUser = this.userDetailsService.loadUserByUsername(source.getClaim("email"));
|
UserDetails springUser = this.userDetailsService.loadUserByUsername(source.getClaimAsString(StandardClaimNames.EMAIL));
|
||||||
return new JwtAuthenticationToken(
|
return new JwtAuthenticationToken(
|
||||||
springUser,
|
springUser,
|
||||||
new ArrayList<>(springUser.getAuthorities()));
|
new ArrayList<>(springUser.getAuthorities()));
|
||||||
|
@@ -29,7 +29,7 @@ import com.activiti.security.identity.service.config.IdentityServiceKeycloakProp
|
|||||||
@Primary
|
@Primary
|
||||||
public class SyncingUserService extends OidcUserService {
|
public class SyncingUserService extends OidcUserService {
|
||||||
|
|
||||||
private final Logger logger = LoggerFactory.getLogger(SyncingUserService.class);
|
private final Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private UserDetailsService userDetailsService;
|
private UserDetailsService userDetailsService;
|
||||||
@@ -43,17 +43,24 @@ public class SyncingUserService extends OidcUserService {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private IdentityServiceKeycloakProperties identityServiceKeycloakProperties;
|
private IdentityServiceKeycloakProperties identityServiceKeycloakProperties;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private TokenRecaller tokenRecaller;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public OidcUser loadUser(OidcUserRequest userRequest) throws OAuth2AuthenticationException {
|
public OidcUser loadUser(OidcUserRequest userRequest) throws OAuth2AuthenticationException {
|
||||||
this.logger.trace("loadUser({}, {})", userRequest.getIdToken().getEmail(), userRequest.getAccessToken().getScopes());
|
this.logger.trace("loadUser({}, {})", userRequest.getIdToken().getEmail(), userRequest.getAccessToken().getScopes());
|
||||||
|
|
||||||
OidcUser oidcUser = super.loadUser(userRequest);
|
OidcUser oidcUser = super.loadUser(userRequest);
|
||||||
this.logger.debug("Loaded OIDC user: {}", oidcUser.getEmail());
|
this.logger.debug("Loaded OIDC user: {}", oidcUser.getEmail());
|
||||||
|
|
||||||
this.userSyncService.sync(oidcUser);
|
if (this.tokenRecaller.recall(userRequest.getAccessToken().getTokenValue())) {
|
||||||
this.groupSyncService.sync(oidcUser);
|
this.logger.trace("Skipping sync for '{}' as the same token was already recently sync'd", userRequest.getIdToken().getEmail());
|
||||||
|
} else {
|
||||||
|
this.userSyncService.sync(oidcUser);
|
||||||
|
this.groupSyncService.sync(oidcUser);
|
||||||
|
this.tokenRecaller.add(userRequest.getAccessToken().getTokenValue());
|
||||||
|
}
|
||||||
|
|
||||||
// reload for sync'd group changes
|
|
||||||
UserDetails springUser = this.userDetailsService.loadUserByUsername(oidcUser.getEmail());
|
UserDetails springUser = this.userDetailsService.loadUserByUsername(oidcUser.getEmail());
|
||||||
|
|
||||||
CustomOAuth2User customOAuth2User = new CustomOAuth2User(
|
CustomOAuth2User customOAuth2User = new CustomOAuth2User(
|
||||||
|
59
src/main/java/com/inteligr8/activiti/auth/service/TokenRecaller.java
Executable file
59
src/main/java/com/inteligr8/activiti/auth/service/TokenRecaller.java
Executable file
@@ -0,0 +1,59 @@
|
|||||||
|
package com.inteligr8.activiti.auth.service;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import org.apache.commons.collections4.map.PassiveExpiringMap;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import jakarta.annotation.PostConstruct;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class TokenRecaller {
|
||||||
|
|
||||||
|
private final Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||||
|
|
||||||
|
@Value("${auth-ext.sync.resyncInMillis:300000}")
|
||||||
|
private long resyncTimeInMillis;
|
||||||
|
|
||||||
|
private PassiveExpiringMap<String, Long> tokenCache;
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
private void init() {
|
||||||
|
this.tokenCache = new PassiveExpiringMap<>(this.resyncTimeInMillis, TimeUnit.MILLISECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Scheduled(timeUnit = TimeUnit.MINUTES, initialDelay = 10L, fixedDelay = 5L)
|
||||||
|
private void reap() {
|
||||||
|
int tokens = this.tokenCache.size();
|
||||||
|
this.logger.trace("Reaping token cache of size: {}", tokens);
|
||||||
|
|
||||||
|
synchronized (this.tokenCache) {
|
||||||
|
// clear expired keys
|
||||||
|
this.tokenCache.entrySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.logger.debug("Reaped {} expired tokens from cache", this.tokenCache.size() - tokens);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(String token) {
|
||||||
|
synchronized (this.tokenCache) {
|
||||||
|
this.tokenCache.put(token, System.currentTimeMillis() + this.resyncTimeInMillis);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean recall(String token) {
|
||||||
|
Long expirationTimeMillis = this.tokenCache.get(token);
|
||||||
|
if (expirationTimeMillis == null) {
|
||||||
|
return false;
|
||||||
|
} else if (expirationTimeMillis < System.currentTimeMillis()) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -101,14 +101,14 @@
|
|||||||
"profile",
|
"profile",
|
||||||
"roles",
|
"roles",
|
||||||
"basic",
|
"basic",
|
||||||
"email"
|
"email",
|
||||||
|
"microprofile-jwt"
|
||||||
],
|
],
|
||||||
"optionalClientScopes": [
|
"optionalClientScopes": [
|
||||||
"address",
|
"address",
|
||||||
"phone",
|
"phone",
|
||||||
"organization",
|
"organization",
|
||||||
"offline_access",
|
"offline_access"
|
||||||
"microprofile-jwt"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -156,15 +156,76 @@
|
|||||||
"profile",
|
"profile",
|
||||||
"roles",
|
"roles",
|
||||||
"basic",
|
"basic",
|
||||||
"email"
|
"email",
|
||||||
|
"microprofile-jwt"
|
||||||
],
|
],
|
||||||
"optionalClientScopes": [
|
"optionalClientScopes": [
|
||||||
"address",
|
"address",
|
||||||
"phone",
|
"phone",
|
||||||
"organization",
|
"organization",
|
||||||
"offline_access",
|
"offline_access"
|
||||||
"microprofile-jwt"
|
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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": [
|
"users": [
|
||||||
|
35
src/test/vscode/simple.http
Normal file
35
src/test/vscode/simple.http
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
@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}}
|
Reference in New Issue
Block a user