/************************************************************************* * * * EJBCA Community: The OpenSource Certificate Authority * * * * This software is free software; you can redistribute it and/or * * modify it under the terms of the GNU Lesser General Public * * License as published by the Free Software Foundation; either * * version 2.1 of the License, or any later version. * * * * See terms of license at gnu.org. * * * *************************************************************************/ package org.ejbca.ra; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.TimeZone; import javax.faces.model.SelectItem; import org.apache.log4j.Logger; import org.cesecore.authentication.tokens.AuthenticationToken; import org.cesecore.authentication.tokens.X509CertificateAuthenticationToken; import org.cesecore.certificates.endentity.EndEntityInformation; import org.cesecore.util.CertTools; import org.cesecore.util.ValidityDate; import org.cesecore.util.ui.DynamicUiProperty; import org.ejbca.core.model.approval.Approval; import org.ejbca.core.model.approval.ApprovalDataText; import org.ejbca.core.model.approval.ApprovalDataVO; import org.ejbca.core.model.approval.ApprovalRequest; import org.ejbca.core.model.approval.TimeAndAdmin; import org.ejbca.core.model.approval.approvalrequests.AddEndEntityApprovalRequest; import org.ejbca.core.model.approval.approvalrequests.EditEndEntityApprovalRequest; import org.ejbca.core.model.approval.profile.ApprovalPartition; import org.ejbca.core.model.approval.profile.ApprovalStep; import org.ejbca.core.model.era.RaApprovalRequestInfo; import org.ejbca.core.model.era.RaApprovalStepInfo; import org.ejbca.core.model.era.RaEditableRequestData; import org.ejbca.core.model.ra.raadmin.EndEntityProfile; /** * Keeps localized information about an approval request. * * @version $Id: ApprovalRequestGUIInfo.java 28562 2018-03-27 14:07:49Z undulf $ */ public class ApprovalRequestGUIInfo implements Serializable { private static final long serialVersionUID = 1L; private static final Logger log = Logger.getLogger(ApprovalRequestGUIInfo.class); public static final class ApprovalGuiObject { private Approval approval; public ApprovalGuiObject(Approval approval) { this.approval = approval; } public String getApprovalDate(){ return ValidityDate.formatAsISO8601(approval.getApprovalDate(), ValidityDate.TIMEZONE_SERVER); } public String getApprovalAdmin(){ return approval.getAdmin().toString(); } public String getAdminAction(){ if(approval.isApproved()){ return "APPROVED"; } return "REJECTED"; } public String getComment(){ return approval.getComment(); } } /** * A display POJO for approval partitions. */ public static final class ApprovalPartitionProfileGuiObject implements Serializable { private static final long serialVersionUID = 1L; private List> profilePropertyList = null; private final int partitionId; private final int stepId; private final List approvals; public ApprovalPartitionProfileGuiObject(final int stepId, final int partitionId, List> propertyValues, List approvals) { //Pass property values as a parameter because it may need some outside poking setProfilePropertyList(propertyValues); this.stepId = stepId; this.partitionId = partitionId; this.approvals = approvals; } public List> getProfilePropertyList() { return profilePropertyList; } public void setProfilePropertyList(List> profilePropertyList) { this.profilePropertyList = profilePropertyList; } public int getPartitionId() { return partitionId; } public int getStepId() { return stepId; } /** @return the current multi-valued property's possible values as JSF friendly SelectItems. */ public List*/> getPropertyPossibleValues( final DynamicUiProperty property) { final List propertyPossibleValues = new ArrayList<>(); if (profilePropertyList != null) { if (property != null && property.getPossibleValues() != null) { for (final Serializable possibleValue : property.getPossibleValues()) { propertyPossibleValues .add(new SelectItem(property.getAsEncodedValue(property.getType().cast(possibleValue)), possibleValue.toString())); } } } return propertyPossibleValues; } public List getApprovals() { return this.approvals; } } public static final class StepOption implements Serializable { private static final long serialVersionUID = 1L; private final String name; private Object value; public StepOption(final String name) { this.name = name; } public String getName() { return name; } public Object getValue() { return value; } public void setValue(final Object value) { this.value = value; } } /** Represents a step that has been approved */ public static final class Step implements Serializable { private static final long serialVersionUID = 1L; private final int stepId; private final Integer stepOrdinal; private final String headingText; private final List partitions; public Step(final RaApprovalStepInfo stepInfo, final RaApprovalRequestInfo request, final RaLocaleBean raLocaleBean) { stepId = stepInfo.getStepId(); final Map stepToOrdinal = request.getStepIdToOrdinalMap(); stepOrdinal = stepToOrdinal.get(stepId); headingText = raLocaleBean.getMessage("view_request_page_step", stepOrdinal); partitions = stepInfo.getPartitions(); } public int getStepId() { return stepId; } public int getStepOrdinal() { if (stepOrdinal==null) { return 0; } return stepOrdinal; } public String getHeadingText() { return headingText; } public List getPartitions() { return partitions; } } public static final class RequestDataRow implements Serializable { private static final long serialVersionUID = 1L; private final ApprovalDataText approvalDataText; private final RaLocaleBean raLocaleBean; private final boolean editingSupported; private Object editValue; // TODO the column maps to a translation id. does it also map to something in the *ApprovalRequest data hashmap? public RequestDataRow(final RaLocaleBean raLocaleBean, final ApprovalDataText approvalDataText, final boolean editingSupported, final Object editValue) { this.approvalDataText = approvalDataText; this.raLocaleBean = raLocaleBean; this.editingSupported = editingSupported; this.editValue = editValue; } public String getKey() { return approvalDataText.getHeader(); } public String getHeader() { if (approvalDataText.isHeaderTranslateable()) { return raLocaleBean.getMessage("view_request_page_data_header_" + approvalDataText.getHeader()); } else { return approvalDataText.getHeader(); } } public String getData() { if (approvalDataText.isDataTranslatable()) { return raLocaleBean.getMessage("view_request_page_data_value_" + approvalDataText.getData()); } else { return approvalDataText.getData(); } } public boolean isEditingSupported() { return editingSupported; } public Object getEditValue() { return editValue; } public void setEditValue(final Object editValue) { this.editValue = editValue; } } // This field is package-internal so RaManageRequest(s)Bean can use it internally. This class is specific to these beans. final RaApprovalRequestInfo request; private final ApprovalDataVO approvalData; private final String requestDate; private final String requestExpireDate; private final String caName; private final String type; private final String requesterName; private final String displayName; private final String detail; private final String status; private final RaEndEntityDetails endEntityDetails; private final List requestData; private final List previousSteps; private final List editLogEntries; // Whether the current admin can approve this request private boolean canApprove; private boolean canEdit; private boolean canView; private final boolean authorizedToRequestType; public ApprovalRequestGUIInfo(final RaApprovalRequestInfo request, final RaLocaleBean raLocaleBean, final RaAccessBean raAccessBean) { this.request = request; approvalData = request.getApprovalData(); // Determine what parts of the approval request are editable final EndEntityInformation endEntityInformation = getEndEntityInformation(); // editable boolean hasEditableData = (endEntityInformation != null); requestData = new ArrayList<>(); if (request.getRequestData() != null && endEntityInformation == null) { final RaEditableRequestData editData = request.getEditableData(); for (final ApprovalDataText dataText : request.getRequestData()) { boolean editingSupported = true; final Object editValue; switch (dataText.getHeader()) { case "SUBJECTDN": editValue = editData.getSubjectDN(); break; case "SUBJECTALTNAME": editValue = editData.getSubjectAltName(); break; case "SUBJECTDIRATTRIBUTES": if ("NOVALUE".equals(dataText.getData())) continue; editValue = editData.getSubjectDirAttrs(); break; case "EMAIL": editValue = editData.getEmail(); break; // Suppress some "no" or "none" values case "HARDTOKENISSUERALIAS": case "KEYRECOVERABLE": case "SENDNOTIFICATION": if ("NOVALUE".equals(dataText.getData()) || "NO".equals(dataText.getData())) continue; // NOPMD: Fall through default: editingSupported = false; editValue = null; } if (editingSupported) { hasEditableData = true; } requestData.add(new RequestDataRow(raLocaleBean, dataText, editingSupported, editValue)); } } requestDate = ValidityDate.formatAsISO8601ServerTZ(approvalData.getRequestDate().getTime(), TimeZone.getDefault()); requestExpireDate = ValidityDate.formatAsISO8601ServerTZ(approvalData.getExpireDate().getTime(), TimeZone.getDefault()); // These must be added last, so the "Extend" button appears under the Expiration Date field. requestData.add(new RequestDataRow(raLocaleBean, new ApprovalDataText("REQUESTDATE", getRequestDate(), true, false), false, null)); requestData.add(new RequestDataRow(raLocaleBean, new ApprovalDataText("REQUESTEXPIRATIONDATE", getRequestExpireDate(), true, false), false, null)); if (approvalData.getCAId() == ApprovalDataVO.ANY_CA) { caName = raLocaleBean.getMessage("manage_requests_no_ca"); } else if (request.getCaName() == null) { caName = "Missing CA id " + approvalData.getCAId(); } else { caName = request.getCaName(); } if (endEntityInformation != null) { final EndEntityProfile endEntityProfile = request.getEndEntityProfile(); final RaEndEntityDetails.Callbacks callbacks = new RaEndEntityDetails.Callbacks() { @Override public RaLocaleBean getRaLocaleBean() { return raLocaleBean; } @Override public EndEntityProfile getEndEntityProfile(int eepId) { return endEntityProfile; } }; endEntityDetails = new RaEndEntityDetails(getEndEntityInformation(), callbacks, request.getCertificateProfileName(), request.getEndEntityProfileName(), caName); } else { endEntityDetails = null; } final String reqSubjDN = request.getRequesterSubjectDN(); if (reqSubjDN != null) { requesterName = getCNOrFallback(reqSubjDN, reqSubjDN); } else { requesterName = ""; } switch (approvalData.getApprovalType()) { case ApprovalDataVO.APPROVALTYPE_ACTIVATECATOKEN: type = raLocaleBean.getMessage("manage_requests_type_activate_ca_token"); break; case ApprovalDataVO.APPROVALTYPE_ADDENDENTITY: type = raLocaleBean.getMessage("manage_requests_type_add_end_entity"); break; case ApprovalDataVO.APPROVALTYPE_CHANGESTATUSENDENTITY: type = raLocaleBean.getMessage("manage_requests_type_change_status_end_entity"); break; case ApprovalDataVO.APPROVALTYPE_EDITENDENTITY: type = raLocaleBean.getMessage("manage_requests_type_edit_end_entity"); break; case ApprovalDataVO.APPROVALTYPE_KEYRECOVERY: type = raLocaleBean.getMessage("manage_requests_type_key_recovery"); break; case ApprovalDataVO.APPROVALTYPE_REVOKEANDDELETEENDENTITY: type = raLocaleBean.getMessage("manage_requests_type_revoke_and_delete_end_entity"); break; case ApprovalDataVO.APPROVALTYPE_REVOKECERTIFICATE: type = raLocaleBean.getMessage("manage_requests_type_revoke_certificate"); break; case ApprovalDataVO.APPROVALTYPE_REVOKEENDENTITY: type = raLocaleBean.getMessage("manage_requests_type_revoke_end_entity"); break; default: log.info("Invalid/unsupported type of approval request: " + approvalData.getApprovalType()); type = "???"; } // Get username and subject DN if the request has this information String username = null; String subjectDN = null; if (endEntityInformation != null) { username = endEntityInformation.getUsername(); subjectDN = endEntityInformation.getDN(); } displayName = getCNOrFallback(subjectDN, username); detail = subjectDN; switch (request.getStatus()) { case ApprovalDataVO.STATUS_APPROVED: status = raLocaleBean.getMessage("manage_requests_status_approved"); break; case ApprovalDataVO.STATUS_EXECUTED: status = raLocaleBean.getMessage("manage_requests_status_executed"); break; case ApprovalDataVO.STATUS_EXECUTIONDENIED: status = raLocaleBean.getMessage("manage_requests_status_execution_denied"); break; case ApprovalDataVO.STATUS_EXECUTIONFAILED: status = raLocaleBean.getMessage("manage_requests_status_execution_failed"); break; case ApprovalDataVO.STATUS_EXPIRED: status = raLocaleBean.getMessage("manage_requests_status_expired"); break; case ApprovalDataVO.STATUS_EXPIREDANDNOTIFIED: status = raLocaleBean.getMessage("manage_requests_status_expired_and_notified"); break; case ApprovalDataVO.STATUS_REJECTED: status = raLocaleBean.getMessage("manage_requests_status_rejected"); break; case ApprovalDataVO.STATUS_WAITINGFORAPPROVAL: status = raLocaleBean.getMessage("manage_requests_status_waiting_for_approval"); break; default: log.info("Invalid status of approval request: " + request.getStatus()); status = "???"; } editLogEntries = new ArrayList<>(); for (final TimeAndAdmin entry : request.getEditedByAdmin()) { final String editDate = ValidityDate.formatAsISO8601(entry.getDate(), TimeZone.getDefault()); final String adminName; if (entry.getAdmin() instanceof X509CertificateAuthenticationToken) { final String adminDN = CertTools.getSubjectDN(((X509CertificateAuthenticationToken)entry.getAdmin()).getCertificate()); adminName = getCNOrFallback(adminDN, adminDN); } else { adminName = entry.getAdmin().toString(); } editLogEntries.add(raLocaleBean.getMessage("view_request_page_edit_log_entry", editDate, adminName)); } if (endEntityInformation != null || approvalData.getApprovalType() == ApprovalDataVO.APPROVALTYPE_REVOKECERTIFICATE || approvalData.getApprovalType() == ApprovalDataVO.APPROVALTYPE_KEYRECOVERY) { authorizedToRequestType = raAccessBean.isAuthorizedToApproveEndEntityRequests(); } else { authorizedToRequestType = raAccessBean.isAuthorizedToApproveCARequests(); } final ApprovalStep nextApprovalStep = request.getNextApprovalStep(); // We can approve our own edits if allowed by approval profile boolean allowSelfEdit = request.getApprovalRequest().getApprovalProfile().getAllowSelfEdit(); canApprove = nextApprovalStep != null && (!request.isEditedByMe() || allowSelfEdit) && !request.isApprovedByMe() && !request.isRequestedByMe() && authorizedToRequestType; // Can only edit our own requests, or requests that we could approve irrespective of who made or edited them. canEdit = authorizedToRequestType && request.isEditable() && hasEditableData && (nextApprovalStep != null || isRequestedByMe()); canView = request.isVisibleToMe(); previousSteps = new ArrayList<>(); for (final RaApprovalStepInfo stepInfo : request.getPreviousApprovalSteps()) { previousSteps.add(new Step(stepInfo, request, raLocaleBean)); } } private String getCNOrFallback(final String subjectDN, final String fallback) { final String cn = CertTools.getPartFromDN(subjectDN, "CN"); if (cn != null) { return cn; } else if (fallback != null) { return fallback; } else { return ""; } } public String getId() { return String.valueOf(request.getId()); } public String getRequestDate() { return requestDate; } public String getRequestExpireDate() { return requestExpireDate; } public String getCa() { return caName; } public String getType() { return type; } public String getRequesterName() { return requesterName; } public String getDisplayName() { return displayName; } public String getDetail() { return detail; } public String getStatus() { return status; } public EndEntityInformation getEndEntityInformation() { final ApprovalRequest approvalRequest = request.getApprovalRequest(); if (approvalRequest instanceof AddEndEntityApprovalRequest) { return ((AddEndEntityApprovalRequest)approvalRequest).getEndEntityInformation(); } else if (approvalRequest instanceof EditEndEntityApprovalRequest) { return ((EditEndEntityApprovalRequest)approvalRequest).getNewEndEntityInformation(); } else { return null; } } public RaEndEntityDetails getEndEntityDetails() { return endEntityDetails; } public List getRequestData() { return requestData; } public List getEditLogEntries() { return editLogEntries; } public List getPreviousSteps() { return previousSteps; } public int getStepCount() { return request.getStepCount(); } public int getCurrentStepOrdinal() { return request.getCurrentStepOrdinal(); } public boolean isCanApprove() { return canApprove; } public boolean isCanEdit() { return canEdit; } public boolean isCanView() {return canView;} public boolean isEditedByMe() { return request.isEditedByMe(); } public boolean isRequestedByMe() { return request.isRequestedByMe(); } public boolean isApprovedByMe() { return request.isApprovedByMe(); } public boolean isPending(final AuthenticationToken admin) { return request.isPending(admin); } public boolean isPendingExecution() { return request.getStatus() == ApprovalDataVO.STATUS_APPROVED; /* = approved but not executed */ } public boolean isExecuted() { return request.getStatus() == ApprovalDataVO.STATUS_EXECUTED; } public boolean isSuccessful() { return isExecuted() || isPendingExecution(); } public boolean isUnsuccessful() { return !isWaitingForApproval() && !isSuccessful(); } public boolean isExecutionFailed() { return request.getStatus() == ApprovalDataVO.STATUS_EXECUTIONFAILED; } public boolean isWaitingForMe(final AuthenticationToken admin) { return request.isWaitingForMe(admin); } public boolean isWaitingForApproval() { return request.getStatus() == ApprovalDataVO.STATUS_WAITINGFORAPPROVAL; } public boolean isExpired() { return request.getStatus() == ApprovalDataVO.STATUS_EXPIRED || request.getStatus() == ApprovalDataVO.STATUS_EXPIREDANDNOTIFIED; } public boolean hasNextApprovalStep() { return request.getNextApprovalStep() != null; } public boolean isAuthorizedToApprovalType() { return authorizedToRequestType; } public boolean getCanExtend() { if (log.isDebugEnabled()) { log.debug("Checking if extension of request expiration is possible: Authorized=" + isAuthorizedToApprovalType() + ", expired=" + isExpired() + ", max extension time=" + request.getMaxExtensionTime()); } return isAuthorizedToApprovalType() && isExpired() && request.getMaxExtensionTime() != 0; } }