001// --------------------------------------------------------------------------------
002// Copyright 2002-2026 Echo Three, LLC
003//
004// Licensed under the Apache License, Version 2.0 (the "License");
005// you may not use this file except in compliance with the License.
006// You may obtain a copy of the License at
007//
008//     http://www.apache.org/licenses/LICENSE-2.0
009//
010// Unless required by applicable law or agreed to in writing, software
011// distributed under the License is distributed on an "AS IS" BASIS,
012// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013// See the License for the specific language governing permissions and
014// limitations under the License.
015// --------------------------------------------------------------------------------
016
017package com.echothree.model.control.workflow.server.logic;
018
019import com.echothree.model.control.security.server.control.SecurityControl;
020import com.echothree.model.control.workflow.server.control.WorkflowControl;
021import com.echothree.model.data.party.common.pk.PartyPK;
022import com.echothree.model.data.party.server.factory.PartyFactory;
023import com.echothree.model.data.workflow.server.entity.WorkflowDestination;
024import com.echothree.model.data.workflow.server.entity.WorkflowEntrance;
025import com.echothree.util.common.message.ExecutionErrors;
026import com.echothree.util.server.message.ExecutionErrorAccumulator;
027import com.echothree.util.server.persistence.EntityPermission;
028import javax.enterprise.context.ApplicationScoped;
029import javax.enterprise.inject.spi.CDI;
030import javax.inject.Inject;
031
032@ApplicationScoped
033public class WorkflowSecurityLogic {
034
035    @Inject
036    PartyFactory partyFactory;
037
038    @Inject
039    SecurityControl securityControl;
040
041    @Inject
042    WorkflowControl workflowControl;
043
044    protected WorkflowSecurityLogic() {
045        super();
046    }
047
048    public static WorkflowSecurityLogic getInstance() {
049        return CDI.current().select(WorkflowSecurityLogic.class).get();
050    }
051    
052    public boolean checkWorkflowEntranceAvailable(final WorkflowEntrance workflowEntrance, final PartyPK partyPK) {
053        var checkPassed = false;
054
055        if(workflowControl.countWorkflowEntrancePartyTypesByWorkflowEntrance(workflowEntrance) != 0) {
056            var party = partyFactory.getEntityFromPK(EntityPermission.READ_ONLY, partyPK);
057            var partyType = party.getLastDetail().getPartyType();
058            var workflowEntrancePartyType = workflowControl.getWorkflowEntrancePartyType(workflowEntrance, partyType);
059
060            if(workflowEntrancePartyType != null) {
061                var workflowEntranceSecurityRoles = workflowControl.getWorkflowEntranceSecurityRolesByWorkflowEntrancePartyType(workflowEntrancePartyType);
062
063                if(workflowEntranceSecurityRoles.isEmpty()) {
064                    // If there are no individual Security Roles, then pass it since the user is in a Party Type that was found.
065                    checkPassed = true;
066                } else {
067                    // Otherwise, check each individual Security Role.
068                    for(var workflowEntranceSecurityRole : workflowEntranceSecurityRoles) {
069                        if(securityControl.partySecurityRoleExists(partyPK, workflowEntranceSecurityRole.getSecurityRolePK())) {
070                            // The Party has one of the required Security Roles, allow the transition and stop further checking.
071                            checkPassed = true;
072                            break;
073                        }
074                    }
075                }
076            }
077        } else {
078            // If there are no Workflow Entrance Party Types, then allow the transition.
079            checkPassed = true;
080        }
081
082        return checkPassed;
083    }
084
085    public boolean checkAddEntityToWorkflow(final ExecutionErrorAccumulator eea, final WorkflowEntrance workflowEntrance, final PartyPK modifiedBy) {
086        var checkPassed = checkWorkflowEntranceAvailable(workflowEntrance, modifiedBy);
087        
088        if(!checkPassed) {
089            eea.addExecutionError(ExecutionErrors.WorkflowEntranceNotAllowed.name());
090        }
091
092        return checkPassed;
093    }
094
095    public boolean checkWorkflowDestinationAvailable(final WorkflowDestination workflowDestination, final PartyPK partyPK) {
096        var checkPassed = false;
097
098        if(workflowControl.countWorkflowDestinationPartyTypes(workflowDestination) != 0) {
099            var party = partyFactory.getEntityFromPK(EntityPermission.READ_ONLY, partyPK);
100            var partyType = party.getLastDetail().getPartyType();
101            var workflowDestinationPartyType = workflowControl.getWorkflowDestinationPartyType(workflowDestination, partyType);
102
103            if(workflowDestinationPartyType != null) {
104                var workflowDestinationSecurityRoles = workflowControl.getWorkflowDestinationSecurityRolesByWorkflowDestinationPartyType(workflowDestinationPartyType);
105
106                if(workflowDestinationSecurityRoles.isEmpty()) {
107                    // If there are no individual Security Roles, then pass it since the user is in a Party Type that was found.
108                    checkPassed = true;
109                } else {
110                    // Otherwise, check each individual Security Role.
111                    for(var workflowDestinationSecurityRole : workflowDestinationSecurityRoles) {
112                        if(securityControl.partySecurityRoleExists(partyPK, workflowDestinationSecurityRole.getSecurityRolePK())) {
113                            // The Party has one of the required Security Roles, allow the transition and stop further checking.
114                            checkPassed = true;
115                            break;
116                        }
117                    }
118                }
119            }
120        } else {
121            // If there are no Workflow Destination Party Types, then allow the transition.
122            checkPassed = true;
123        }
124
125        return checkPassed;
126    }
127
128    public boolean checkTransitionEntityInWorkflow(final ExecutionErrorAccumulator eea, final WorkflowDestination workflowDestination, final PartyPK modifiedBy) {
129        var checkPassed = checkWorkflowDestinationAvailable(workflowDestination, modifiedBy);
130
131        if(!checkPassed) {
132            eea.addExecutionError(ExecutionErrors.WorkflowDestinationNotAllowed.name());
133        }
134
135        return checkPassed;
136    }
137
138}