-
Notifications
You must be signed in to change notification settings - Fork 619
Feature - Flow Extension Executor (stacked on #8109) #8112
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
acd491e
c3fe3b0
ea66f24
f77b29d
4444b23
93c87ce
dbedb57
ea0efce
2400b67
1150b2c
7654677
df4857a
4db0026
f88134c
b587758
3c01603
7386607
dbfa893
62173db
ff0d3fe
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -18,9 +18,18 @@ | |||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| package org.wso2.carbon.identity.action.execution.api.model; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| import com.fasterxml.jackson.annotation.JsonInclude; | ||||||||||||||||||||||||||||
| import com.fasterxml.jackson.core.JsonGenerator; | ||||||||||||||||||||||||||||
| import com.fasterxml.jackson.databind.JsonSerializer; | ||||||||||||||||||||||||||||
| import com.fasterxml.jackson.databind.SerializerProvider; | ||||||||||||||||||||||||||||
| import com.fasterxml.jackson.databind.annotation.JsonSerialize; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| import java.io.IOException; | ||||||||||||||||||||||||||||
| import java.util.ArrayList; | ||||||||||||||||||||||||||||
| import java.util.Collections; | ||||||||||||||||||||||||||||
| import java.util.HashMap; | ||||||||||||||||||||||||||||
| import java.util.List; | ||||||||||||||||||||||||||||
| import java.util.Map; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||
| * This class models the User. | ||||||||||||||||||||||||||||
|
|
@@ -30,9 +39,11 @@ public class User { | |||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| private final String id; | ||||||||||||||||||||||||||||
| private final List<UserClaim> claims = new ArrayList<>(); | ||||||||||||||||||||||||||||
| private final Map<String, char[]> userCredentials = new HashMap<>(); | ||||||||||||||||||||||||||||
| private final List<String> groups = new ArrayList<>(); | ||||||||||||||||||||||||||||
| private final List<String> roles = new ArrayList<>(); | ||||||||||||||||||||||||||||
| private Organization organization; | ||||||||||||||||||||||||||||
| private UserStore userStoreDomain; | ||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||
| * Represents the user id when the user is shared across sub-organizations. | ||||||||||||||||||||||||||||
| * This field differs from the regular user id ({@link #id}) in scenarios where a user is accessed in the context | ||||||||||||||||||||||||||||
|
|
@@ -65,15 +76,18 @@ public User(Builder builder) { | |||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| this.id = builder.id; | ||||||||||||||||||||||||||||
| this.claims.addAll(builder.claims); | ||||||||||||||||||||||||||||
| this.userCredentials.putAll(builder.userCredentials); | ||||||||||||||||||||||||||||
| this.groups.addAll(builder.groups); | ||||||||||||||||||||||||||||
| this.roles.addAll(builder.roles); | ||||||||||||||||||||||||||||
| this.organization = builder.organization; | ||||||||||||||||||||||||||||
| this.sharedUserId = builder.sharedUserId; | ||||||||||||||||||||||||||||
| this.userType = builder.userType; | ||||||||||||||||||||||||||||
| this.federatedIdP = builder.federatedIdP; | ||||||||||||||||||||||||||||
| this.accessingOrganization = builder.accessingOrganization; | ||||||||||||||||||||||||||||
| this.userStoreDomain = builder.userStoreDomain; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| @JsonInclude(JsonInclude.Include.NON_NULL) | ||||||||||||||||||||||||||||
| public String getId() { | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| return id; | ||||||||||||||||||||||||||||
|
|
@@ -84,6 +98,11 @@ public List<UserClaim> getClaims() { | |||||||||||||||||||||||||||
| return Collections.unmodifiableList(claims); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| public Map<String, char[]> getUserCredentials() { | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| return Collections.unmodifiableMap(userCredentials); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| public List<String> getGroups() { | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| return Collections.unmodifiableList(groups); | ||||||||||||||||||||||||||||
|
|
@@ -99,6 +118,13 @@ public Organization getOrganization() { | |||||||||||||||||||||||||||
| return organization; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| @JsonInclude(JsonInclude.Include.NON_NULL) | ||||||||||||||||||||||||||||
| @JsonSerialize(using = UserStoreNameSerializer.class) | ||||||||||||||||||||||||||||
| public UserStore getUserStoreDomain() { | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| return userStoreDomain; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| public String getSharedUserId() { | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| return sharedUserId; | ||||||||||||||||||||||||||||
|
|
@@ -126,9 +152,11 @@ public static class Builder { | |||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| private final String id; | ||||||||||||||||||||||||||||
| private final List<UserClaim> claims = new ArrayList<>(); | ||||||||||||||||||||||||||||
| private final Map<String, char[]> userCredentials = new HashMap<>(); | ||||||||||||||||||||||||||||
| private final List<String> groups = new ArrayList<>(); | ||||||||||||||||||||||||||||
| private final List<String> roles = new ArrayList<>(); | ||||||||||||||||||||||||||||
| private Organization organization; | ||||||||||||||||||||||||||||
| private UserStore userStoreDomain; | ||||||||||||||||||||||||||||
| private String sharedUserId; | ||||||||||||||||||||||||||||
| private String userType; | ||||||||||||||||||||||||||||
| private String federatedIdP; | ||||||||||||||||||||||||||||
|
|
@@ -163,6 +191,12 @@ public Builder organization(Organization organization) { | |||||||||||||||||||||||||||
| return this; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| public Builder userStoreDomain(UserStore userStoreDomain) { | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| this.userStoreDomain = userStoreDomain; | ||||||||||||||||||||||||||||
| return this; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| public Builder sharedUserId(String sharedUserId) { | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| this.sharedUserId = sharedUserId; | ||||||||||||||||||||||||||||
|
|
@@ -187,9 +221,24 @@ public Builder accessingOrganization(Organization accessingOrganization) { | |||||||||||||||||||||||||||
| return this; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| public Builder userCredentials(Map<String, char[]> userCredentials) { | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| this.userCredentials.putAll(userCredentials); | ||||||||||||||||||||||||||||
| return this; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| public User build() { | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| return new User(this); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| private static class UserStoreNameSerializer extends JsonSerializer<UserStore> { | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| @Override | ||||||||||||||||||||||||||||
| public void serialize(UserStore value, JsonGenerator gen, SerializerProvider serializers) throws IOException { | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| gen.writeString(value.getName() != null ? value.getName() : ""); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
Comment on lines
+238
to
+242
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Log Improvement Suggestion No: 1
Suggested change
|
||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -154,6 +154,12 @@ | |||||||||||||||||||||||||||||
| } catch (ActionExecutionRuntimeException e) { | ||||||||||||||||||||||||||||||
| LOG.debug("Skip executing action for action type: " + actionType.name(), e); | ||||||||||||||||||||||||||||||
| // Skip executing actions when no action available is considered as action execution being successful. | ||||||||||||||||||||||||||||||
| Action.ActionTypes.Category category = Action.ActionTypes.valueOf(actionType.toString()).getCategory(); | ||||||||||||||||||||||||||||||
| if (Action.ActionTypes.Category.FLOW_EXTENSION.equals(category)) { | ||||||||||||||||||||||||||||||
| throw new ActionExecutionException( | ||||||||||||||||||||||||||||||
| "Failed to execute flow extension action with id: " + actionId | ||||||||||||||||||||||||||||||
|
Comment on lines
154
to
+160
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Log Improvement Suggestion No: 2
Suggested change
|
||||||||||||||||||||||||||||||
| + " for action type: " + actionType.name(), e); | ||||||||||||||||||||||||||||||
|
Check failure on line 161 in components/action-mgt/org.wso2.carbon.identity.action.execution/src/main/java/org/wso2/carbon/identity/action/execution/internal/service/impl/ActionExecutorServiceImpl.java
|
||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| return new SuccessStatus.Builder().setResponseContext(flowContext.getContextData()).build(); | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -87,6 +87,8 @@ public boolean isExecutionForActionTypeEnabled(ActionType actionType) { | |||||||||||||||||||||||||||||||||||||||||
| return isActionTypeEnabled(ActionTypeConfig.PRE_UPDATE_PROFILE.getActionTypeEnableProperty()); | ||||||||||||||||||||||||||||||||||||||||||
| case PRE_ISSUE_ID_TOKEN: | ||||||||||||||||||||||||||||||||||||||||||
| return isActionTypeEnabled(ActionTypeConfig.PRE_ISSUE_ID_TOKEN.getActionTypeEnableProperty()); | ||||||||||||||||||||||||||||||||||||||||||
| case FLOW_EXTENSION: | ||||||||||||||||||||||||||||||||||||||||||
| return isActionTypeEnabled(ActionTypeConfig.FLOW_EXTENSION.getActionTypeEnableProperty()); | ||||||||||||||||||||||||||||||||||||||||||
| default: | ||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
89
to
92
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Log Improvement Suggestion No: 3
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||
| return false; | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -333,6 +335,8 @@ public String getRetiredUpToVersion(ActionType actionType) { | |||||||||||||||||||||||||||||||||||||||||
| return getVersion(ActionTypeConfig.PRE_UPDATE_PROFILE.getRetiredUpToVersionProperty()); | ||||||||||||||||||||||||||||||||||||||||||
| case PRE_ISSUE_ID_TOKEN: | ||||||||||||||||||||||||||||||||||||||||||
| return getVersion(ActionTypeConfig.PRE_ISSUE_ID_TOKEN.getRetiredUpToVersionProperty()); | ||||||||||||||||||||||||||||||||||||||||||
| case FLOW_EXTENSION: | ||||||||||||||||||||||||||||||||||||||||||
| return getVersion(ActionTypeConfig.FLOW_EXTENSION.getRetiredUpToVersionProperty()); | ||||||||||||||||||||||||||||||||||||||||||
| default: | ||||||||||||||||||||||||||||||||||||||||||
| return null; | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -417,7 +421,13 @@ private enum ActionTypeConfig { | |||||||||||||||||||||||||||||||||||||||||
| "Actions.Types.PreIssueIdToken.ActionRequest.ExcludedParameters.Parameter", | ||||||||||||||||||||||||||||||||||||||||||
| "Actions.Types.PreIssueIdToken.ActionRequest.AllowedHeaders.Header", | ||||||||||||||||||||||||||||||||||||||||||
| "Actions.Types.PreIssueIdToken.ActionRequest.AllowedParameters.Parameter", | ||||||||||||||||||||||||||||||||||||||||||
| "Actions.Types.PreIssueIdToken.Version.RetiredUpTo"); | ||||||||||||||||||||||||||||||||||||||||||
| "Actions.Types.PreIssueIdToken.Version.RetiredUpTo"), | ||||||||||||||||||||||||||||||||||||||||||
| FLOW_EXTENSION("Actions.Types.FlowExtension.Enable", | ||||||||||||||||||||||||||||||||||||||||||
| "Actions.Types.FlowExtension.ActionRequest.ExcludedHeaders.Header", | ||||||||||||||||||||||||||||||||||||||||||
| "Actions.Types.FlowExtension.ActionRequest.ExcludedParameters.Parameter", | ||||||||||||||||||||||||||||||||||||||||||
| "Actions.Types.FlowExtension.ActionRequest.AllowedHeaders.Header", | ||||||||||||||||||||||||||||||||||||||||||
| "Actions.Types.FlowExtension.ActionRequest.AllowedParameters.Parameter", | ||||||||||||||||||||||||||||||||||||||||||
| "Actions.Types.FlowExtension.Version.RetiredUpTo"); | ||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+425
to
+430
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
🔧 Suggested fix@@ public Set<String> getExcludedHeadersInActionRequestForActionType(ActionType actionType) {
switch (actionType) {
case PRE_ISSUE_ACCESS_TOKEN:
excludedHeadersPropertyValue = getPropertyValues(
ActionTypeConfig.PRE_ISSUE_ACCESS_TOKEN.getExcludedHeadersProperty());
break;
+ case FLOW_EXTENSION:
+ excludedHeadersPropertyValue = getPropertyValues(
+ ActionTypeConfig.FLOW_EXTENSION.getExcludedHeadersProperty());
+ break;
default:
break;
}
@@ public Set<String> getExcludedParamsInActionRequestForActionType(ActionType actionType) {
switch (actionType) {
case PRE_ISSUE_ACCESS_TOKEN:
excludedParamsPropertyValue = getPropertyValues(
ActionTypeConfig.PRE_ISSUE_ACCESS_TOKEN.getExcludedParamsProperty());
break;
+ case FLOW_EXTENSION:
+ excludedParamsPropertyValue = getPropertyValues(
+ ActionTypeConfig.FLOW_EXTENSION.getExcludedParamsProperty());
+ break;
default:
break;
}
@@ public Set<String> getAllowedHeadersForActionType(ActionType actionType) {
switch (actionType) {
case PRE_ISSUE_ACCESS_TOKEN:
allowedPropertyKey = ActionTypeConfig.PRE_ISSUE_ACCESS_TOKEN.getAllowedHeaderProperty();
break;
+ case FLOW_EXTENSION:
+ allowedPropertyKey = ActionTypeConfig.FLOW_EXTENSION.getAllowedHeaderProperty();
+ break;
default:
break;
}
@@ public Set<String> getAllowedParamsForActionType(ActionType actionType) {
switch (actionType) {
case PRE_ISSUE_ACCESS_TOKEN:
allowedPropertyKey = ActionTypeConfig.PRE_ISSUE_ACCESS_TOKEN.getAllowedParamsProperty();
break;
+ case FLOW_EXTENSION:
+ allowedPropertyKey = ActionTypeConfig.FLOW_EXTENSION.getAllowedParamsProperty();
+ break;
default:
break;
}🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| private final String actionTypeEnableProperty; | ||||||||||||||||||||||||||||||||||||||||||
| private final String excludedHeadersProperty; | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -39,15 +39,7 @@ private ActionConverterFactory() { | |||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| public static ActionConverter getActionConverter(Action.ActionTypes actionType) { | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| switch (actionType) { | ||||||||||||||||||||||||||||||||||||||||||
| case PRE_UPDATE_PROFILE: | ||||||||||||||||||||||||||||||||||||||||||
| return actionConverters.get(Action.ActionTypes.PRE_UPDATE_PROFILE); | ||||||||||||||||||||||||||||||||||||||||||
| case PRE_UPDATE_PASSWORD: | ||||||||||||||||||||||||||||||||||||||||||
| return actionConverters.get(Action.ActionTypes.PRE_UPDATE_PASSWORD); | ||||||||||||||||||||||||||||||||||||||||||
| case PRE_ISSUE_ACCESS_TOKEN: | ||||||||||||||||||||||||||||||||||||||||||
| default: | ||||||||||||||||||||||||||||||||||||||||||
| return null; | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| return actionConverters.get(actionType); | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
40
to
43
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Log Improvement Suggestion No: 4
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| public static void registerActionConverter(ActionConverter actionConverter) { | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -26,6 +26,7 @@ | |||||||||||||||
| import org.wso2.carbon.identity.action.management.api.exception.ActionMgtException; | ||||||||||||||||
| import org.wso2.carbon.identity.action.management.api.exception.ActionMgtServerException; | ||||||||||||||||
| import org.wso2.carbon.identity.action.management.api.model.Action; | ||||||||||||||||
| import org.wso2.carbon.identity.action.management.api.model.Action.ActionTypes; | ||||||||||||||||
| import org.wso2.carbon.identity.action.management.api.model.ActionDTO; | ||||||||||||||||
| import org.wso2.carbon.identity.action.management.api.model.Authentication; | ||||||||||||||||
| import org.wso2.carbon.identity.action.management.api.model.EndpointConfig; | ||||||||||||||||
|
|
@@ -76,6 +77,7 @@ public Action addAction(String actionType, Action action, String tenantDomain) t | |||||||||||||||
| Action.ActionTypes castedActionType = Action.ActionTypes.valueOf(resolvedActionType); | ||||||||||||||||
| ActionValidatorFactory.getActionValidator(castedActionType).doPreAddActionValidations( | ||||||||||||||||
| castedActionType, ActionManagementConfig.getInstance().getLatestVersion(castedActionType), action); | ||||||||||||||||
|
Comment on lines
77
to
79
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Log Improvement Suggestion No: 5
Suggested change
|
||||||||||||||||
| validateActionNameUniqueness(action.getName(), null, castedActionType, tenantId); | ||||||||||||||||
| // Check whether the maximum allowed actions per type is reached. | ||||||||||||||||
| validateMaxActionsPerType(resolvedActionType, tenantDomain); | ||||||||||||||||
| String generatedActionId = UUID.randomUUID().toString(); | ||||||||||||||||
|
|
@@ -161,6 +163,7 @@ public Action updateAction(String actionType, String actionId, Action action, St | |||||||||||||||
| Action.ActionTypes castedActionType = Action.ActionTypes.valueOf(resolvedActionType); | ||||||||||||||||
| ActionValidatorFactory.getActionValidator(castedActionType).doPreUpdateActionValidations( | ||||||||||||||||
| castedActionType, resolveActionVersionAtUpdating(action, existingActionDTO), action); | ||||||||||||||||
| validateActionNameUniqueness(action.getName(), actionId, castedActionType, tenantId); | ||||||||||||||||
| ActionDTO updatingActionDTO = buildActionDTOForUpdate(resolvedActionType, actionId, action); | ||||||||||||||||
|
|
||||||||||||||||
| DAO_FACADE.updateAction(updatingActionDTO, existingActionDTO, tenantId); | ||||||||||||||||
|
|
@@ -317,8 +320,10 @@ private String getActionTypeFromPath(String actionType) throws ActionMgtClientEx | |||||||||||||||
| */ | ||||||||||||||||
| private void validateMaxActionsPerType(String actionType, String tenantDomain) throws ActionMgtException { | ||||||||||||||||
|
|
||||||||||||||||
| // In-flow actions are not limited by the maximum actions per action type; eg: AUTHENTICATION action type. | ||||||||||||||||
| if (Action.ActionTypes.Category.IN_FLOW.equals(Action.ActionTypes.valueOf(actionType).getCategory())) { | ||||||||||||||||
| // In-flow and extension actions are not limited by the maximum actions per action type. | ||||||||||||||||
| Action.ActionTypes.Category category = Action.ActionTypes.valueOf(actionType).getCategory(); | ||||||||||||||||
| if (Action.ActionTypes.Category.IN_FLOW.equals(category) | ||||||||||||||||
| || Action.ActionTypes.Category.FLOW_EXTENSION.equals(category)) { | ||||||||||||||||
| return; | ||||||||||||||||
| } | ||||||||||||||||
| Map<String, Integer> actionsCountPerType = getActionsCountPerType(tenantDomain); | ||||||||||||||||
|
|
@@ -365,8 +370,13 @@ private ActionDTO buildActionDTOForCreation(String actionType, String actionId, | |||||||||||||||
| throws ActionMgtServerException { | ||||||||||||||||
|
|
||||||||||||||||
| Action.ActionTypes resolvedActionType = Action.ActionTypes.valueOf(actionType); | ||||||||||||||||
| Action.Status resolvedStatus = resolvedActionType.getCategory() == Action.ActionTypes.Category.IN_FLOW ? | ||||||||||||||||
| Action.Status.ACTIVE : Action.Status.INACTIVE; | ||||||||||||||||
| // Only IN_FLOW and FLOW_EXTENSION category actions (e.g., AUTHENTICATION, FLOW_EXTENSION) | ||||||||||||||||
| // start ACTIVE and can be used immediately. All other categories (e.g., PRE_POST) start | ||||||||||||||||
| // INACTIVE and require explicit activation. | ||||||||||||||||
| Action.ActionTypes.Category category = resolvedActionType.getCategory(); | ||||||||||||||||
| Action.Status resolvedStatus = (category == Action.ActionTypes.Category.IN_FLOW | ||||||||||||||||
| || category == Action.ActionTypes.Category.FLOW_EXTENSION) | ||||||||||||||||
| ? Action.Status.ACTIVE : Action.Status.INACTIVE; | ||||||||||||||||
|
|
||||||||||||||||
| String actionVersion = ActionManagementConfig.getInstance().getLatestVersion(resolvedActionType); | ||||||||||||||||
|
|
||||||||||||||||
|
|
@@ -470,4 +480,25 @@ private Action buildAction(String actionType, ActionDTO actionDTO) { | |||||||||||||||
| .rule(actionDTO.getActionRule()) | ||||||||||||||||
| .build(); | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| private void validateActionNameUniqueness(String name, String excludeId, ActionTypes actionType, int tenantId) | ||||||||||||||||
| throws ActionMgtException { | ||||||||||||||||
|
|
||||||||||||||||
| if (!ActionTypes.FLOW_EXTENSION.equals(actionType)) { | ||||||||||||||||
| return; | ||||||||||||||||
| } | ||||||||||||||||
| if (StringUtils.isBlank(name)) { | ||||||||||||||||
| throw ActionManagementExceptionHandler.handleClientException( | ||||||||||||||||
| ErrorMessage.ERROR_ACTION_NAME_BLANK); | ||||||||||||||||
| } | ||||||||||||||||
|
Comment on lines
+490
to
+493
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. PATCH updates are incorrectly rejected when Line 166 always invokes uniqueness validation, and Line 490 rejects blank names. That breaks the method’s PATCH contract (null/empty fields should be ignored) and can fail partial updates that do not include Proposed fix- if (StringUtils.isBlank(name)) {
- throw ActionManagementExceptionHandler.handleClientException(
- ErrorMessage.ERROR_ACTION_NAME_BLANK);
- }
+ if (StringUtils.isBlank(name)) {
+ // PATCH update path: omitted/blank fields are ignored.
+ if (excludeId != null) {
+ return;
+ }
+ throw ActionManagementExceptionHandler.handleClientException(
+ ErrorMessage.ERROR_ACTION_NAME_BLANK);
+ }🤖 Prompt for AI Agents |
||||||||||||||||
| List<ActionDTO> existingActions = DAO_FACADE.getActionsByActionType(actionType.getActionType(), tenantId); | ||||||||||||||||
| boolean duplicateExists = existingActions.stream() | ||||||||||||||||
| .filter(dto -> excludeId == null || !excludeId.equals(dto.getId())) | ||||||||||||||||
| .anyMatch(dto -> name.equalsIgnoreCase(dto.getName())); | ||||||||||||||||
|
|
||||||||||||||||
| if (duplicateExists) { | ||||||||||||||||
| throw ActionManagementExceptionHandler.handleClientException( | ||||||||||||||||
| ErrorMessage.ERROR_ACTION_NAME_ALREADY_EXISTS, name, actionType.getActionType()); | ||||||||||||||||
| } | ||||||||||||||||
|
Comment on lines
+500
to
+502
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Log Improvement Suggestion No: 6
Suggested change
|
||||||||||||||||
| } | ||||||||||||||||
| } | ||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -94,7 +94,11 @@ public String getLatestVersion(ActionTypes actionType) throws ActionMgtServerExc | |||||||||||||||||||||||||||
| return getVersion( | ||||||||||||||||||||||||||||
| ActionTypeConfig.PRE_UPDATE_PROFILE.getLatestVersionProperty(), actionType); | ||||||||||||||||||||||||||||
| case PRE_ISSUE_ID_TOKEN: | ||||||||||||||||||||||||||||
| return getVersion(ActionTypeConfig.PRE_ISSUE_ID_TOKEN.getLatestVersionProperty(), actionType); | ||||||||||||||||||||||||||||
| return getVersion( | ||||||||||||||||||||||||||||
| ActionTypeConfig.PRE_ISSUE_ID_TOKEN.getLatestVersionProperty(), actionType); | ||||||||||||||||||||||||||||
| case FLOW_EXTENSION: | ||||||||||||||||||||||||||||
| return getVersion( | ||||||||||||||||||||||||||||
| ActionTypeConfig.FLOW_EXTENSION.getLatestVersionProperty(), actionType); | ||||||||||||||||||||||||||||
|
Comment on lines
96
to
+101
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Log Improvement Suggestion No: 7
Suggested change
|
||||||||||||||||||||||||||||
| default: | ||||||||||||||||||||||||||||
| throw new ActionMgtServerException("Unsupported action type: " + actionType); | ||||||||||||||||||||||||||||
|
Comment on lines
102
to
103
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Log Improvement Suggestion No: 8
Suggested change
|
||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
@@ -140,6 +144,11 @@ public enum ActionTypeConfig { | |||||||||||||||||||||||||||
| "Actions.Types.PreIssueIdToken.ActionRequest.ExcludedHeaders.Header", | ||||||||||||||||||||||||||||
| "Actions.Types.PreIssueIdToken.ActionRequest.ExcludedParameters.Parameter", | ||||||||||||||||||||||||||||
| "Actions.Types.PreIssueIdToken.Version.Latest" | ||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||
| FLOW_EXTENSION( | ||||||||||||||||||||||||||||
| "Actions.Types.FlowExtension.ActionRequest.ExcludedHeaders.Header", | ||||||||||||||||||||||||||||
| null, | ||||||||||||||||||||||||||||
| "Actions.Types.FlowExtension.Version.Latest" | ||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| private final String excludedHeadersProperty; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Defensively copy
userCredentialsto prevent mutable credential leakage.Current
putAll/unmodifiableMaphandling is shallow. External code can still mutate the storedchar[]values, andBuilder.userCredentials(...)can NPE on null input.🔧 Suggested fix
Also applies to: 101-104, 224-227
🤖 Prompt for AI Agents