001// --------------------------------------------------------------------------------
002// Copyright 2002-2025 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 com.echothree.util.server.persistence.Session;
029import javax.enterprise.context.ApplicationScoped;
030import javax.enterprise.inject.spi.CDI;
031
032@ApplicationScoped
033public class WorkflowSecurityLogic {
034
035    protected WorkflowSecurityLogic() {
036        super();
037    }
038
039    public static WorkflowSecurityLogic getInstance() {
040        return CDI.current().select(WorkflowSecurityLogic.class).get();
041    }
042    
043    public boolean checkWorkflowEntranceAvailable(final WorkflowEntrance workflowEntrance, final PartyPK partyPK) {
044        var workflowControl = Session.getModelController(WorkflowControl.class);
045        var checkPassed = false;
046
047        if(workflowControl.countWorkflowEntrancePartyTypesByWorkflowEntrance(workflowEntrance) != 0) {
048            var party = PartyFactory.getInstance().getEntityFromPK(EntityPermission.READ_ONLY, partyPK);
049            var partyType = party.getLastDetail().getPartyType();
050            var workflowEntrancePartyType = workflowControl.getWorkflowEntrancePartyType(workflowEntrance, partyType);
051
052            if(workflowEntrancePartyType != null) {
053                var securityControl = Session.getModelController(SecurityControl.class);
054                var workflowEntranceSecurityRoles = workflowControl.getWorkflowEntranceSecurityRolesByWorkflowEntrancePartyType(workflowEntrancePartyType);
055
056                if(workflowEntranceSecurityRoles.isEmpty()) {
057                    // If there are no individual Security Roles, then pass it since the user is in a Party Type that was found.
058                    checkPassed = true;
059                } else {
060                    // Otherwise, check each individual Security Role.
061                    for(var workflowEntranceSecurityRole : workflowEntranceSecurityRoles) {
062                        if(securityControl.partySecurityRoleExists(partyPK, workflowEntranceSecurityRole.getSecurityRolePK())) {
063                            // The Party has one of the required Security Roles, allow the transition and stop further checking.
064                            checkPassed = true;
065                            break;
066                        }
067                    }
068                }
069            }
070        } else {
071            // If there are no Workflow Entrance Party Types, then allow the transition.
072            checkPassed = true;
073        }
074
075        return checkPassed;
076    }
077
078    public boolean checkAddEntityToWorkflow(final ExecutionErrorAccumulator eea, final WorkflowEntrance workflowEntrance, final PartyPK modifiedBy) {
079        var checkPassed = checkWorkflowEntranceAvailable(workflowEntrance, modifiedBy);
080        
081        if(!checkPassed) {
082            eea.addExecutionError(ExecutionErrors.WorkflowEntranceNotAllowed.name());
083        }
084
085        return checkPassed;
086    }
087
088    public boolean checkWorkflowDestinationAvailable(final WorkflowDestination workflowDestination, final PartyPK partyPK) {
089        var workflowControl = Session.getModelController(WorkflowControl.class);
090        var checkPassed = false;
091
092        if(workflowControl.countWorkflowDestinationPartyTypes(workflowDestination) != 0) {
093            var party = PartyFactory.getInstance().getEntityFromPK(EntityPermission.READ_ONLY, partyPK);
094            var partyType = party.getLastDetail().getPartyType();
095            var workflowDestinationPartyType = workflowControl.getWorkflowDestinationPartyType(workflowDestination, partyType);
096
097            if(workflowDestinationPartyType != null) {
098                var securityControl = Session.getModelController(SecurityControl.class);
099                var workflowDestinationSecurityRoles = workflowControl.getWorkflowDestinationSecurityRolesByWorkflowDestinationPartyType(workflowDestinationPartyType);
100
101                if(workflowDestinationSecurityRoles.isEmpty()) {
102                    // If there are no individual Security Roles, then pass it since the user is in a Party Type that was found.
103                    checkPassed = true;
104                } else {
105                    // Otherwise, check each individual Security Role.
106                    for(var workflowDestinationSecurityRole : workflowDestinationSecurityRoles) {
107                        if(securityControl.partySecurityRoleExists(partyPK, workflowDestinationSecurityRole.getSecurityRolePK())) {
108                            // The Party has one of the required Security Roles, allow the transition and stop further checking.
109                            checkPassed = true;
110                            break;
111                        }
112                    }
113                }
114            }
115        } else {
116            // If there are no Workflow Destination Party Types, then allow the transition.
117            checkPassed = true;
118        }
119
120        return checkPassed;
121    }
122
123    public boolean checkTransitionEntityInWorkflow(final ExecutionErrorAccumulator eea, final WorkflowDestination workflowDestination, final PartyPK modifiedBy) {
124        var checkPassed = checkWorkflowDestinationAvailable(workflowDestination, modifiedBy);
125
126        if(!checkPassed) {
127            eea.addExecutionError(ExecutionErrors.WorkflowDestinationNotAllowed.name());
128        }
129
130        return checkPassed;
131    }
132
133}