diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/wf/WorkItemsPanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/wf/WorkItemsPanel.java index e83fa7ecec2..42d8ec02990 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/wf/WorkItemsPanel.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/wf/WorkItemsPanel.java @@ -28,6 +28,8 @@ import com.evolveum.midpoint.web.component.data.column.IconColumn; import com.evolveum.midpoint.web.component.data.column.LinkColumn; import com.evolveum.midpoint.web.page.admin.workflow.PageWorkItem; +import com.evolveum.midpoint.web.page.admin.workflow.PageWorkItems; +import com.evolveum.midpoint.web.page.admin.workflow.dto.ProtectedWorkItemId; import com.evolveum.midpoint.web.page.admin.workflow.dto.WorkItemDtoProvider; import com.evolveum.midpoint.web.page.admin.workflow.dto.WorkItemDto; import com.evolveum.midpoint.web.session.UserProfileStorage; @@ -163,7 +165,8 @@ private AbstractColumn createNameColumn() { @Override public void onClick(AjaxRequestTarget target, IModel rowModel) { PageParameters parameters = new PageParameters(); - parameters.add(OnePageParameterEncoder.PARAMETER, rowModel.getObject().getWorkItemId()); + parameters.add(OnePageParameterEncoder.PARAMETER, + ProtectedWorkItemId.createExternalForm(rowModel.getObject().getWorkItem())); getPageBase().navigateToNext(PageWorkItem.class, parameters); } }; diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/workflow/PageWorkItem.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/workflow/PageWorkItem.java index 85f20394bf4..58a7ae3bab7 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/workflow/PageWorkItem.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/workflow/PageWorkItem.java @@ -43,6 +43,7 @@ import com.evolveum.midpoint.web.component.DefaultAjaxSubmitButton; import com.evolveum.midpoint.web.component.util.VisibleBehaviour; import com.evolveum.midpoint.web.page.admin.workflow.dto.WorkItemDto; +import com.evolveum.midpoint.web.page.admin.workflow.dto.ProtectedWorkItemId; import com.evolveum.midpoint.web.util.OnePageParameterEncoder; import com.evolveum.midpoint.xml.ns._public.common.common_3.*; import org.apache.wicket.RestartResponseException; @@ -99,12 +100,12 @@ public class PageWorkItem extends PageAdminWorkItems { private static final String ID_CANCEL = "cancel"; private LoadableModel workItemDtoModel; - private String taskId; + private String externalizedProtectedId; public PageWorkItem(PageParameters parameters) { - taskId = parameters.get(OnePageParameterEncoder.PARAMETER).toString(); - if (taskId == null) { + externalizedProtectedId = parameters.get(OnePageParameterEncoder.PARAMETER).toString(); + if (externalizedProtectedId == null) { throw new IllegalStateException("Work item ID not specified."); } @@ -131,25 +132,27 @@ private WorkItemDto loadWorkItemDtoIfNecessary() { OperationResult result = task.getResult(); WorkItemDto workItemDto = null; try { - final ObjectQuery query = QueryBuilder.queryFor(WorkItemType.class, getPrismContext()) - .item(F_EXTERNAL_ID).eq(taskId) + ProtectedWorkItemId protectedWorkItemId = ProtectedWorkItemId.fromExternalForm(externalizedProtectedId); + final ObjectQuery query = QueryBuilder.queryFor(WorkItemType.class, getPrismContext()) + .item(F_EXTERNAL_ID).eq(protectedWorkItemId.id) .build(); final Collection> options = resolveItemsNamed(F_ASSIGNEE_REF, F_ORIGINAL_ASSIGNEE_REF); List workItems = getModelService().searchContainers(WorkItemType.class, query, options, task, result); if (workItems.size() > 1) { - throw new SystemException("More than one work item with ID of " + taskId); + throw new SystemException("More than one work item with ID of " + protectedWorkItemId.id); } else if (workItems.size() == 0) { - throw new ObjectNotFoundException("No work item with ID of " + taskId); + throw new ObjectNotFoundException("No work item with ID of " + protectedWorkItemId.id); } final WorkItemType workItem = workItems.get(0); - - final String taskOid = WfContextUtil.getTaskOid(workItem); + String taskOid = WfContextUtil.getTaskOid(workItem); if (taskOid == null) { // this is a problem ... most probably we will not be able to do anything reasonable - let's give it up result.recordFatalError(getString("PageWorkItem.noRequest")); showResult(result, false); throw redirectBackViaRestartResponseException(); + } else if (!protectedWorkItemId.isCorrect(workItem)) { + throw new IllegalArgumentException("Wrong work item hash"); } TaskType taskType = null; List relatedTasks = new ArrayList<>(); diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/workflow/dto/ProtectedWorkItemId.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/workflow/dto/ProtectedWorkItemId.java new file mode 100644 index 00000000000..9ebdd331974 --- /dev/null +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/page/admin/workflow/dto/ProtectedWorkItemId.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2010-2019 Evolveum + * + * 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 com.evolveum.midpoint.web.page.admin.workflow.dto; + +import com.evolveum.midpoint.prism.xml.XmlTypeConverter; +import com.evolveum.midpoint.schema.util.WfContextUtil; +import com.evolveum.midpoint.util.MiscUtil; +import com.evolveum.midpoint.util.exception.SystemException; +import com.evolveum.midpoint.xml.ns._public.common.common_3.WorkItemType; +import org.jetbrains.annotations.NotNull; + +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +/** + * Protected version of work item ID in the form of "id:hash". + * + * It is to be used as part of URL used to display the work item. The hash is computed from selected parts of + * the work item so it is impossible to guess. + */ +public class ProtectedWorkItemId { + + @NotNull public final String id; + @NotNull public final String hash; + + private ProtectedWorkItemId(@NotNull String id, @NotNull String hash) { + this.id = id; + this.hash = hash; + } + + public static ProtectedWorkItemId fromExternalForm(@NotNull String externalForm) { + int i = externalForm.indexOf(':'); + if (i < 0) { + throw new IllegalArgumentException("Wrong work item ID format"); + } + return new ProtectedWorkItemId(externalForm.substring(0, i), externalForm.substring(i+1)); + } + + private static String createWorkItemHash(WorkItemType workItem) { + try { + String valueToHash = workItem.getExternalId() + ":" + + WfContextUtil.getTaskOid(workItem) + ":" + + XmlTypeConverter.toMillis(workItem.getCreateTimestamp()); + byte[] hashBytes = MessageDigest.getInstance("SHA-256").digest(valueToHash.getBytes(StandardCharsets.UTF_8)); + return MiscUtil.binaryToHex(hashBytes); + } catch (NoSuchAlgorithmException e) { + throw new SystemException("Couldn't compute message digest: " + e.getMessage(), e); + } + } + + public static String createExternalForm(WorkItemType workItem) { + return workItem.getExternalId() + ":" + createWorkItemHash(workItem); + } + + public boolean isCorrect(WorkItemType workItem) { + return hash.equals(createWorkItemHash(workItem)); + } +}